diff options
author | Sebastian Andrzej Siewior <bigeasy@linutronix.de> | 2017-07-05 12:17:20 +0200 |
---|---|---|
committer | Sebastian Andrzej Siewior <bigeasy@linutronix.de> | 2017-07-05 12:17:20 +0200 |
commit | a78f57db7375bc1933199a6ee0913cd41d0ff2f0 (patch) | |
tree | 3e5fa2135c773bbe942c493f0300033df82cde82 | |
parent | d2c7e5df37b8b915818c2443aff48b6a6270f0bf (diff) | |
download | 4.12-rt-patches-a78f57db7375bc1933199a6ee0913cd41d0ff2f0.tar.gz |
[ANNOUNCE] v4.11.8-rt5
Dear RT folks!
I'm pleased to announce the v4.11.8-rt5 patch set.
Changes since v4.11.8-rt4:
- Merged Tom Zanussi's "tracing: Inter-event (e.g. latency) support"
series. As a result I dropped the old "latency histogram" patches
we had in tree which provided more or less the thing (which means
the following config symbols are gone: INTERRUPT_OFF_HIST,
PREEMPT_OFF_HIST, WAKEUP_LATENCY_HIST, MISSED_TIMER_OFFSETS_HIST).
Known issues
- CPU hotplug got a little better but can deadlock.
The delta patch against v4.11.8-rt4 will be sent as a reply to this mail
and can be found here:
https://cdn.kernel.org/pub/linux/kernel/projects/rt/4.11/incr/patch-4.11.8-rt4-rt5.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.8-rt5
The RT patch against v4.11.8 can be found here:
https://cdn.kernel.org/pub/linux/kernel/projects/rt/4.11/older/patch-4.11.8-rt5.patch.xz
The split quilt queue is available at:
https://cdn.kernel.org/pub/linux/kernel/projects/rt/4.11/older/patches-4.11.8-rt5.tar.xz
Sebastian
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
145 files changed, 9405 insertions, 2205 deletions
diff --git a/patches/0001-cpu-hotplug-Provide-cpus_read-write_-un-lock.patch b/patches/0001-cpu-hotplug-Provide-cpus_read-write_-un-lock.patch index 3352c259f1f61..242dd583b1905 100644 --- a/patches/0001-cpu-hotplug-Provide-cpus_read-write_-un-lock.patch +++ b/patches/0001-cpu-hotplug-Provide-cpus_read-write_-un-lock.patch @@ -1,4 +1,3 @@ -From 8f553c498e1772cccb39a114da4a498d22992758 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:12 +0200 Subject: [PATCH 01/32] cpu/hotplug: Provide cpus_read|write_[un]lock() diff --git a/patches/0001-futex-Avoid-freeing-an-active-timer.patch b/patches/0001-futex-Avoid-freeing-an-active-timer.patch index 617dcc5378d11..edc05daba7674 100644 --- a/patches/0001-futex-Avoid-freeing-an-active-timer.patch +++ b/patches/0001-futex-Avoid-freeing-an-active-timer.patch @@ -1,4 +1,3 @@ -From 97181f9bd57405b879403763284537e27d46963d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Mon, 10 Apr 2017 18:03:36 +0200 Subject: [PATCH 1/4] futex: Avoid freeing an active timer diff --git a/patches/0001-ia64-topology-Remove-cpus_allowed-manipulation.patch b/patches/0001-ia64-topology-Remove-cpus_allowed-manipulation.patch index 5a79929a77902..d04096847c3c7 100644 --- a/patches/0001-ia64-topology-Remove-cpus_allowed-manipulation.patch +++ b/patches/0001-ia64-topology-Remove-cpus_allowed-manipulation.patch @@ -1,4 +1,3 @@ -From 048c9b954e20396e0c45ee778466994d1be2e612 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 12 Apr 2017 22:07:27 +0200 Subject: [PATCH 01/13] ia64/topology: Remove cpus_allowed manipulation diff --git a/patches/0001-init-Pin-init-task-to-the-boot-CPU-initially.patch b/patches/0001-init-Pin-init-task-to-the-boot-CPU-initially.patch index 25730fb718c41..7a806149e3a19 100644 --- a/patches/0001-init-Pin-init-task-to-the-boot-CPU-initially.patch +++ b/patches/0001-init-Pin-init-task-to-the-boot-CPU-initially.patch @@ -1,4 +1,3 @@ -From 8fb12156b8db61af3d49f3e5e104568494581d1f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:32 +0200 Subject: [PATCH 01/17] init: Pin init task to the boot CPU, initially diff --git a/patches/0001-rtmutex-Deboost-before-waking-up-the-top-waiter.patch b/patches/0001-rtmutex-Deboost-before-waking-up-the-top-waiter.patch index ede43101e953d..b3202e76bd89d 100644 --- a/patches/0001-rtmutex-Deboost-before-waking-up-the-top-waiter.patch +++ b/patches/0001-rtmutex-Deboost-before-waking-up-the-top-waiter.patch @@ -1,4 +1,3 @@ -From 2a1c6029940675abb2217b590512dbf691867ec4 Mon Sep 17 00:00:00 2001 From: Xunlei Pang <xlpang@redhat.com> Date: Thu, 23 Mar 2017 15:56:07 +0100 Subject: [PATCH 1/9] rtmutex: Deboost before waking up the top waiter diff --git a/patches/0001-sched-clock-Fix-early-boot-preempt-assumption-in-__s.patch b/patches/0001-sched-clock-Fix-early-boot-preempt-assumption-in-__s.patch index 4d1f4d4e226a1..9072fe63ca6d6 100644 --- a/patches/0001-sched-clock-Fix-early-boot-preempt-assumption-in-__s.patch +++ b/patches/0001-sched-clock-Fix-early-boot-preempt-assumption-in-__s.patch @@ -1,4 +1,3 @@ -From 45aea321678856687927c53972321ebfab77759a Mon Sep 17 00:00:00 2001 From: Peter Zijlstra <peterz@infradead.org> Date: Wed, 24 May 2017 08:52:02 +0200 Subject: [PATCH] sched/clock: Fix early boot preempt assumption in diff --git a/patches/0001-tracing-Add-hist_field_name-accessor.patch b/patches/0001-tracing-Add-hist_field_name-accessor.patch new file mode 100644 index 0000000000000..f934609d44c01 --- /dev/null +++ b/patches/0001-tracing-Add-hist_field_name-accessor.patch @@ -0,0 +1,175 @@ +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 + +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 +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(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -146,6 +146,23 @@ struct hist_trigger_data { + struct tracing_map *map; + }; + ++static const char *hist_field_name(struct hist_field *field, ++ unsigned int level) ++{ ++ const char *field_name = ""; ++ ++ if (level > 1) ++ return field_name; ++ ++ if (field->field) ++ field_name = field->field->name; ++ ++ if (field_name == NULL) ++ field_name = ""; ++ ++ return field_name; ++} ++ + 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 + static int create_sort_keys(struct hist_trigger_data *hist_data) + { + char *fields_str = hist_data->attrs->sort_key_str; +- struct ftrace_event_field *field = NULL; + 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_ + } + + for (i = 0; i < TRACING_MAP_SORT_KEYS_MAX; i++) { ++ struct hist_field *hist_field; + char *field_str, *field_name; ++ const char *test_name; + + sort_key = &hist_data->sort_keys[i]; + +@@ -703,8 +721,11 @@ static int create_sort_keys(struct hist_ + } + + for (j = 1; j < hist_data->n_fields; j++) { +- field = hist_data->fields[j]->field; +- 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 + struct hist_field *key_field; + char str[KSYM_SYMBOL_LEN]; + bool multiline = false; ++ const char *field_name; + unsigned int i; + u64 uval; + +@@ -963,26 +985,27 @@ hist_trigger_entry_print(struct seq_file + if (i > hist_data->n_vals) + seq_puts(m, ", "); + ++ field_name = hist_field_name(key_field, 0); ++ + if (key_field->flags & HIST_FIELD_FL_HEX) { + uval = *(u64 *)(key + key_field->offset); +- seq_printf(m, "%s: %llx", +- key_field->field->name, uval); ++ seq_printf(m, "%s: %llx", field_name, uval); + } else if (key_field->flags & HIST_FIELD_FL_SYM) { + uval = *(u64 *)(key + key_field->offset); + sprint_symbol_no_offset(str, uval); +- seq_printf(m, "%s: [%llx] %-45s", +- key_field->field->name, uval, str); ++ seq_printf(m, "%s: [%llx] %-45s", field_name, ++ uval, str); + } else if (key_field->flags & HIST_FIELD_FL_SYM_OFFSET) { + uval = *(u64 *)(key + key_field->offset); + sprint_symbol(str, uval); +- seq_printf(m, "%s: [%llx] %-55s", +- key_field->field->name, uval, str); ++ seq_printf(m, "%s: [%llx] %-55s", field_name, ++ uval, str); + } else if (key_field->flags & HIST_FIELD_FL_EXECNAME) { + char *comm = elt->private_data; + + uval = *(u64 *)(key + key_field->offset); +- seq_printf(m, "%s: %-16s[%10llu]", +- key_field->field->name, comm, uval); ++ seq_printf(m, "%s: %-16s[%10llu]", field_name, ++ comm, uval); + } else if (key_field->flags & HIST_FIELD_FL_SYSCALL) { + const char *syscall_name; + +@@ -991,8 +1014,8 @@ hist_trigger_entry_print(struct seq_file + if (!syscall_name) + syscall_name = "unknown_syscall"; + +- seq_printf(m, "%s: %-30s[%3llu]", +- key_field->field->name, syscall_name, uval); ++ seq_printf(m, "%s: %-30s[%3llu]", field_name, ++ syscall_name, uval); + } 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 + HIST_STACKTRACE_DEPTH); + multiline = true; + } else if (key_field->flags & HIST_FIELD_FL_LOG2) { +- seq_printf(m, "%s: ~ 2^%-2llu", key_field->field->name, ++ seq_printf(m, "%s: ~ 2^%-2llu", field_name, + *(u64 *)(key + key_field->offset)); + } else if (key_field->flags & HIST_FIELD_FL_STRING) { +- seq_printf(m, "%s: %-50s", key_field->field->name, ++ seq_printf(m, "%s: %-50s", field_name, + (char *)(key + key_field->offset)); + } else { + uval = *(u64 *)(key + key_field->offset); +- seq_printf(m, "%s: %10llu", key_field->field->name, +- uval); ++ seq_printf(m, "%s: %10llu", field_name, uval); + } + } + +@@ -1021,13 +1043,13 @@ hist_trigger_entry_print(struct seq_file + tracing_map_read_sum(elt, HITCOUNT_IDX)); + + for (i = 1; i < hist_data->n_vals; i++) { ++ field_name = hist_field_name(hist_data->fields[i], 0); ++ + if (hist_data->fields[i]->flags & HIST_FIELD_FL_HEX) { +- seq_printf(m, " %s: %10llx", +- hist_data->fields[i]->field->name, ++ seq_printf(m, " %s: %10llx", field_name, + tracing_map_read_sum(elt, i)); + } else { +- seq_printf(m, " %s: %10llu", +- hist_data->fields[i]->field->name, ++ seq_printf(m, " %s: %10llu", field_name, + tracing_map_read_sum(elt, i)); + } + } +@@ -1142,7 +1164,9 @@ static const char *get_hist_field_flags( + + static void hist_field_print(struct seq_file *m, struct hist_field *hist_field) + { +- seq_printf(m, "%s", hist_field->field->name); ++ const char *field_name = hist_field_name(hist_field, 0); ++ ++ seq_printf(m, "%s", field_name); + if (hist_field->flags) { + const char *flags_str = get_hist_field_flags(hist_field); + diff --git a/patches/0002-arm-Adjust-system_state-check.patch b/patches/0002-arm-Adjust-system_state-check.patch index 89388ec06c6f3..78888688cee1b 100644 --- a/patches/0002-arm-Adjust-system_state-check.patch +++ b/patches/0002-arm-Adjust-system_state-check.patch @@ -1,4 +1,3 @@ -From 5976a66913a8bf42465d96776fd37fb5631edc19 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:33 +0200 Subject: [PATCH 02/17] arm: Adjust system_state check diff --git a/patches/0002-cpu-hotplug-Provide-lockdep_assert_cpus_held.patch b/patches/0002-cpu-hotplug-Provide-lockdep_assert_cpus_held.patch index 8739b951c37dc..9e6b2015c19aa 100644 --- a/patches/0002-cpu-hotplug-Provide-lockdep_assert_cpus_held.patch +++ b/patches/0002-cpu-hotplug-Provide-lockdep_assert_cpus_held.patch @@ -1,4 +1,3 @@ -From ade3f680a76b474d9f5375a9b1d100ee787bf469 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:13 +0200 Subject: [PATCH 02/32] cpu/hotplug: Provide lockdep_assert_cpus_held() diff --git a/patches/0002-futex-Fix-small-and-harmless-looking-inconsistencies.patch b/patches/0002-futex-Fix-small-and-harmless-looking-inconsistencies.patch index 374ace45a5af6..cdd80c952bd07 100644 --- a/patches/0002-futex-Fix-small-and-harmless-looking-inconsistencies.patch +++ b/patches/0002-futex-Fix-small-and-harmless-looking-inconsistencies.patch @@ -1,4 +1,3 @@ -From 94ffac5d847cfd790bb37b7cef1cad803743985e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra <peterz@infradead.org> Date: Fri, 7 Apr 2017 09:04:07 +0200 Subject: [PATCH 2/4] futex: Fix small (and harmless looking) inconsistencies diff --git a/patches/0002-sched-rtmutex-deadline-Fix-a-PI-crash-for-deadline-t.patch b/patches/0002-sched-rtmutex-deadline-Fix-a-PI-crash-for-deadline-t.patch index 46d5775c97124..be4a67d767309 100644 --- a/patches/0002-sched-rtmutex-deadline-Fix-a-PI-crash-for-deadline-t.patch +++ b/patches/0002-sched-rtmutex-deadline-Fix-a-PI-crash-for-deadline-t.patch @@ -1,4 +1,3 @@ -From e96a7705e7d3fef96aec9b590c63b2f6f7d2ba22 Mon Sep 17 00:00:00 2001 From: Xunlei Pang <xlpang@redhat.com> Date: Thu, 23 Mar 2017 15:56:08 +0100 Subject: [PATCH 2/9] sched/rtmutex/deadline: Fix a PI crash for deadline tasks diff --git a/patches/0002-tracing-Reimplement-log2.patch b/patches/0002-tracing-Reimplement-log2.patch new file mode 100644 index 0000000000000..0d674beb6c9ff --- /dev/null +++ b/patches/0002-tracing-Reimplement-log2.patch @@ -0,0 +1,114 @@ +From: Tom Zanussi <tom.zanussi@linux.intel.com> +Date: Mon, 26 Jun 2017 17:49:03 -0500 +Subject: [PATCH 02/32] 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 +field. + +To prepare for synthetic fields like latencies, log2 should be +applicable to those as well, so take the opportunity now to fix the +current problems as well as expand to more general uses. + +log2 should be thought of as a chaining function rather than a field +type. To enable this as well as possible future function +implementations, add a hist_field operand array into the hist_field +definition for this purpose, and make use of it to implement the log2 +'function'. + +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 | 31 +++++++++++++++++++++++++++---- + 1 file changed, 27 insertions(+), 4 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -28,12 +28,16 @@ struct hist_field; + + typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event); + ++#define HIST_FIELD_OPERANDS_MAX 2 ++ + struct hist_field { + struct ftrace_event_field *field; + unsigned long flags; + hist_field_fn_t fn; + unsigned int size; + unsigned int offset; ++ unsigned int is_signed; ++ struct hist_field *operands[HIST_FIELD_OPERANDS_MAX]; + }; + + static u64 hist_field_none(struct hist_field *field, void *event) +@@ -71,7 +75,9 @@ static u64 hist_field_pstring(struct his + + static u64 hist_field_log2(struct hist_field *hist_field, void *event) + { +- u64 val = *(u64 *)(event + hist_field->field->offset); ++ struct hist_field *operand = hist_field->operands[0]; ++ ++ u64 val = operand->fn(operand, event); + + return (u64) ilog2(roundup_pow_of_two(val)); + } +@@ -156,6 +162,8 @@ static const char *hist_field_name(struc + + if (field->field) + field_name = field->field->name; ++ else if (field->flags & HIST_FIELD_FL_LOG2) ++ field_name = hist_field_name(field->operands[0], ++level); + + if (field_name == NULL) + field_name = ""; +@@ -357,8 +365,20 @@ static const struct tracing_map_ops hist + .elt_init = hist_trigger_elt_comm_init, + }; + +-static void destroy_hist_field(struct hist_field *hist_field) ++static void destroy_hist_field(struct hist_field *hist_field, ++ unsigned int level) + { ++ unsigned int i; ++ ++ if (level > 2) ++ return; ++ ++ if (!hist_field) ++ return; ++ ++ for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++) ++ destroy_hist_field(hist_field->operands[i], ++level); ++ + kfree(hist_field); + } + +@@ -385,7 +405,10 @@ 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; ++ hist_field->operands[0] = create_hist_field(field, fl); ++ hist_field->size = hist_field->operands[0]->size; + goto out; + } + +@@ -405,7 +428,7 @@ static struct hist_field *create_hist_fi + hist_field->fn = select_value_fn(field->size, + field->is_signed); + if (!hist_field->fn) { +- destroy_hist_field(hist_field); ++ destroy_hist_field(hist_field, 0); + return NULL; + } + } +@@ -422,7 +445,7 @@ static void destroy_hist_fields(struct h + + for (i = 0; i < TRACING_MAP_FIELDS_MAX; i++) { + if (hist_data->fields[i]) { +- destroy_hist_field(hist_data->fields[i]); ++ destroy_hist_field(hist_data->fields[i], 0); + hist_data->fields[i] = NULL; + } + } diff --git a/patches/0002-workqueue-Provide-work_on_cpu_safe.patch b/patches/0002-workqueue-Provide-work_on_cpu_safe.patch index c134fbc223b58..80a6bef1dfea1 100644 --- a/patches/0002-workqueue-Provide-work_on_cpu_safe.patch +++ b/patches/0002-workqueue-Provide-work_on_cpu_safe.patch @@ -1,4 +1,3 @@ -From 0e8d6a9336b487a1dd6f1991ff376e669d4c87c6 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 12 Apr 2017 22:07:28 +0200 Subject: [PATCH 02/13] workqueue: Provide work_on_cpu_safe() diff --git a/patches/0003-arm64-Adjust-system_state-check.patch b/patches/0003-arm64-Adjust-system_state-check.patch index 72f3d175f194b..3382d580c7ca1 100644 --- a/patches/0003-arm64-Adjust-system_state-check.patch +++ b/patches/0003-arm64-Adjust-system_state-check.patch @@ -1,4 +1,3 @@ -From ef284f5ca5f102bf855e599305c0c16d6e844635 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:34 +0200 Subject: [PATCH 03/17] arm64: Adjust system_state check diff --git a/patches/0003-cpu-hotplug-Provide-cpuhp_setup-remove_state-_nocall.patch b/patches/0003-cpu-hotplug-Provide-cpuhp_setup-remove_state-_nocall.patch index 6b98814eb2f14..9d0373376ec2c 100644 --- a/patches/0003-cpu-hotplug-Provide-cpuhp_setup-remove_state-_nocall.patch +++ b/patches/0003-cpu-hotplug-Provide-cpuhp_setup-remove_state-_nocall.patch @@ -1,4 +1,3 @@ -From 71def423fe3da0d40ad3427a4cd5f9edc53bff67 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Wed, 24 May 2017 10:15:14 +0200 Subject: [PATCH 03/32] cpu/hotplug: Provide diff --git a/patches/0003-futex-Clarify-mark_wake_futex-memory-barrier-usage.patch b/patches/0003-futex-Clarify-mark_wake_futex-memory-barrier-usage.patch index 311c2d0ddc7b5..397a43b09a533 100644 --- a/patches/0003-futex-Clarify-mark_wake_futex-memory-barrier-usage.patch +++ b/patches/0003-futex-Clarify-mark_wake_futex-memory-barrier-usage.patch @@ -1,4 +1,3 @@ -From 38fcd06e9b7f6855db1f3ebac5e18b8fdb467ffd Mon Sep 17 00:00:00 2001 From: "Darren Hart (VMware)" <dvhart@infradead.org> Date: Fri, 14 Apr 2017 15:31:38 -0700 Subject: [PATCH 3/4] futex: Clarify mark_wake_futex memory barrier usage diff --git a/patches/0003-ia64-salinfo-Replace-racy-task-affinity-logic.patch b/patches/0003-ia64-salinfo-Replace-racy-task-affinity-logic.patch index 80d6d4dcd8233..0af2d70b7fd60 100644 --- a/patches/0003-ia64-salinfo-Replace-racy-task-affinity-logic.patch +++ b/patches/0003-ia64-salinfo-Replace-racy-task-affinity-logic.patch @@ -1,4 +1,3 @@ -From 67cb85fdcee7fbc61c09c00360d1a4ae37641db4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 12 Apr 2017 22:07:29 +0200 Subject: [PATCH 03/13] ia64/salinfo: Replace racy task affinity logic diff --git a/patches/0003-ring-buffer-Add-interface-for-setting-absolute-time-.patch b/patches/0003-ring-buffer-Add-interface-for-setting-absolute-time-.patch new file mode 100644 index 0000000000000..edc0f2f13e653 --- /dev/null +++ b/patches/0003-ring-buffer-Add-interface-for-setting-absolute-time-.patch @@ -0,0 +1,114 @@ +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 + stamps + +Define a new function, tracing_set_time_stamp_abs(), which can be used +to enable or disable the use of absolute timestamps rather than time +deltas for a trace array. + +This resets the buffer to prevent a mix of time deltas and absolute +timestamps. + +Only the interface is added here; a subsequent patch will add the +underlying implementation. + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +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(-) + +--- a/include/linux/ring_buffer.h ++++ b/include/linux/ring_buffer.h +@@ -180,6 +180,8 @@ void ring_buffer_normalize_time_stamp(st + int cpu, u64 *ts); + void ring_buffer_set_clock(struct ring_buffer *buffer, + u64 (*clock)(void)); ++void ring_buffer_set_time_stamp_abs(struct ring_buffer *buffer, bool abs); ++bool ring_buffer_time_stamp_abs(struct ring_buffer *buffer); + + size_t ring_buffer_page_len(void *page); + +--- a/kernel/trace/ring_buffer.c ++++ b/kernel/trace/ring_buffer.c +@@ -484,6 +484,7 @@ struct ring_buffer { + u64 (*clock)(void); + + struct rb_irq_work irq_work; ++ bool time_stamp_abs; + }; + + struct ring_buffer_iter { +@@ -1378,6 +1379,16 @@ void ring_buffer_set_clock(struct ring_b + buffer->clock = clock; + } + ++void ring_buffer_set_time_stamp_abs(struct ring_buffer *buffer, bool abs) ++{ ++ buffer->time_stamp_abs = abs; ++} ++ ++bool ring_buffer_time_stamp_abs(struct ring_buffer *buffer) ++{ ++ return buffer->time_stamp_abs; ++} ++ + static void rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer); + + static inline unsigned long rb_page_entries(struct buffer_page *bpage) +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -2082,7 +2082,7 @@ trace_event_buffer_lock_reserve(struct r + + *current_rb = trace_file->tr->trace_buffer.buffer; + +- if ((trace_file->flags & ++ if (!ring_buffer_time_stamp_abs(*current_rb) && (trace_file->flags & + (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 + return ret; + } + ++int tracing_set_time_stamp_abs(struct trace_array *tr, bool abs) ++{ ++ mutex_lock(&trace_types_lock); ++ ++ ring_buffer_set_time_stamp_abs(tr->trace_buffer.buffer, abs); ++ ++ /* ++ * New timestamps may not be consistent with the previous setting. ++ * Reset the buffer so that it doesn't have incomparable timestamps. ++ */ ++ tracing_reset_online_cpus(&tr->trace_buffer); ++ ++#ifdef CONFIG_TRACER_MAX_TRACE ++ if (tr->flags & TRACE_ARRAY_FL_GLOBAL && tr->max_buffer.buffer) ++ ring_buffer_set_time_stamp_abs(tr->max_buffer.buffer, abs); ++ tracing_reset_online_cpus(&tr->max_buffer); ++#endif ++ ++ mutex_unlock(&trace_types_lock); ++ ++ return 0; ++} ++ + 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; + extern int trace_array_get(struct trace_array *tr); + extern void trace_array_put(struct trace_array *tr); + ++extern int tracing_set_time_stamp_abs(struct trace_array *tr, bool abs); ++ + /* + * The global tracer (top) should be the first trace array added, + * but we check the flag anyway. diff --git a/patches/0003-sched-deadline-rtmutex-Dont-miss-the-dl_runtime-dl_p.patch b/patches/0003-sched-deadline-rtmutex-Dont-miss-the-dl_runtime-dl_p.patch index ed71fb48348c9..2e395cdb66f01 100644 --- a/patches/0003-sched-deadline-rtmutex-Dont-miss-the-dl_runtime-dl_p.patch +++ b/patches/0003-sched-deadline-rtmutex-Dont-miss-the-dl_runtime-dl_p.patch @@ -1,4 +1,3 @@ -From 85e2d4f992868ad78dc8bb2c077b652fcfb3661a Mon Sep 17 00:00:00 2001 From: Xunlei Pang <xlpang@redhat.com> Date: Thu, 23 Mar 2017 15:56:09 +0100 Subject: [PATCH 3/9] sched/deadline/rtmutex: Dont miss the diff --git a/patches/0004-MAINTAINERS-Add-FUTEX-SUBSYSTEM.patch b/patches/0004-MAINTAINERS-Add-FUTEX-SUBSYSTEM.patch index a47fbc0f9b566..2606f7f54f302 100644 --- a/patches/0004-MAINTAINERS-Add-FUTEX-SUBSYSTEM.patch +++ b/patches/0004-MAINTAINERS-Add-FUTEX-SUBSYSTEM.patch @@ -1,4 +1,3 @@ -From 59cd42c29618c45cd3c56da43402b14f611888dd Mon Sep 17 00:00:00 2001 From: "Darren Hart (VMware)" <dvhart@infradead.org> Date: Fri, 14 Apr 2017 15:46:08 -0700 Subject: [PATCH 4/4] MAINTAINERS: Add FUTEX SUBSYSTEM diff --git a/patches/0004-cpu-hotplug-Add-__cpuhp_state_add_instance_cpuslocke.patch b/patches/0004-cpu-hotplug-Add-__cpuhp_state_add_instance_cpuslocke.patch index 1cb080124b7df..adf931932e62a 100644 --- a/patches/0004-cpu-hotplug-Add-__cpuhp_state_add_instance_cpuslocke.patch +++ b/patches/0004-cpu-hotplug-Add-__cpuhp_state_add_instance_cpuslocke.patch @@ -1,4 +1,3 @@ -From 9805c6733349ea3ccd22cf75b8ebaabb5290e310 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:15 +0200 Subject: [PATCH 04/32] cpu/hotplug: Add diff --git a/patches/0004-ia64-sn-hwperf-Replace-racy-task-affinity-logic.patch b/patches/0004-ia64-sn-hwperf-Replace-racy-task-affinity-logic.patch index 41a9a46f629db..040d433e5b10a 100644 --- a/patches/0004-ia64-sn-hwperf-Replace-racy-task-affinity-logic.patch +++ b/patches/0004-ia64-sn-hwperf-Replace-racy-task-affinity-logic.patch @@ -1,4 +1,3 @@ -From 9feb42ac88b516e378b9782e82b651ca5bed95c4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Thu, 6 Apr 2017 14:56:18 +0200 Subject: [PATCH 04/13] ia64/sn/hwperf: Replace racy task affinity logic diff --git a/patches/0004-ring-buffer-Redefine-the-unimplemented-RINGBUF_TIME_.patch b/patches/0004-ring-buffer-Redefine-the-unimplemented-RINGBUF_TIME_.patch new file mode 100644 index 0000000000000..d7ad02d26f167 --- /dev/null +++ b/patches/0004-ring-buffer-Redefine-the-unimplemented-RINGBUF_TIME_.patch @@ -0,0 +1,330 @@ +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 + RINGBUF_TIME_TIME_STAMP + +RINGBUF_TYPE_TIME_STAMP is defined but not used, and from what I can +gather was reserved for something like an absolute timestamp feature +for the ring buffer, if not a complete replacement of the current +time_delta scheme. + +This code redefines RINGBUF_TYPE_TIME_STAMP to implement absolute time +stamps. Another way to look at it is that it essentially forces +extended time_deltas for all events. + +The motivation for doing this is to enable time_deltas that aren't +dependent on previous events in the ring buffer, making it feasible to +use the ring_buffer_event timetamps in a more random-access way, for +purposes other than serial event printing. + +To set/reset this mode, use tracing_set_timestamp_abs() from the +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(-) + +--- a/include/linux/ring_buffer.h ++++ b/include/linux/ring_buffer.h +@@ -36,10 +36,12 @@ struct ring_buffer_event { + * array[0] = time delta (28 .. 59) + * size = 8 bytes + * +- * @RINGBUF_TYPE_TIME_STAMP: Sync time stamp with external clock +- * array[0] = tv_nsec +- * array[1..2] = tv_sec +- * size = 16 bytes ++ * @RINGBUF_TYPE_TIME_STAMP: Absolute timestamp ++ * Same format as TIME_EXTEND except that the ++ * value is an absolute timestamp, not a delta ++ * event.time_delta contains bottom 27 bits ++ * array[0] = top (28 .. 59) bits ++ * size = 8 bytes + * + * <= @RINGBUF_TYPE_DATA_TYPE_LEN_MAX: + * Data record +@@ -56,12 +58,12 @@ enum ring_buffer_type { + RINGBUF_TYPE_DATA_TYPE_LEN_MAX = 28, + RINGBUF_TYPE_PADDING, + RINGBUF_TYPE_TIME_EXTEND, +- /* FIXME: RINGBUF_TYPE_TIME_STAMP not implemented */ + RINGBUF_TYPE_TIME_STAMP, + }; + + unsigned ring_buffer_event_length(struct ring_buffer_event *event); + void *ring_buffer_event_data(struct ring_buffer_event *event); ++u64 ring_buffer_event_time_stamp(struct ring_buffer_event *event); + + /* + * ring_buffer_discard_commit will remove an event that has not +--- a/kernel/trace/ring_buffer.c ++++ b/kernel/trace/ring_buffer.c +@@ -42,6 +42,8 @@ int ring_buffer_print_entry_header(struc + RINGBUF_TYPE_PADDING); + trace_seq_printf(s, "\ttime_extend : type == %d\n", + RINGBUF_TYPE_TIME_EXTEND); ++ trace_seq_printf(s, "\ttime_stamp : type == %d\n", ++ RINGBUF_TYPE_TIME_STAMP); + trace_seq_printf(s, "\tdata max type_len == %d\n", + RINGBUF_TYPE_DATA_TYPE_LEN_MAX); + +@@ -147,6 +149,9 @@ enum { + #define skip_time_extend(event) \ + ((struct ring_buffer_event *)((char *)event + RB_LEN_TIME_EXTEND)) + ++#define extended_time(event) \ ++ (event->type_len >= RINGBUF_TYPE_TIME_EXTEND) ++ + 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 + { + unsigned len = 0; + +- if (event->type_len == RINGBUF_TYPE_TIME_EXTEND) { ++ if (extended_time(event)) { + /* 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 + { + unsigned length; + +- if (event->type_len == RINGBUF_TYPE_TIME_EXTEND) ++ if (extended_time(event)) + event = skip_time_extend(event); + + length = rb_event_length(event); +@@ -249,7 +252,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_event_leng + static __always_inline void * + rb_event_data(struct ring_buffer_event *event) + { +- if (event->type_len == RINGBUF_TYPE_TIME_EXTEND) ++ if (extended_time(event)) + 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 + #define TS_MASK ((1ULL << TS_SHIFT) - 1) + #define TS_DELTA_TEST (~TS_MASK) + ++/** ++ * ring_buffer_event_time_stamp - return the event's extended timestamp ++ * @event: the event to get the timestamp of ++ * ++ * Returns the extended timestamp associated with a data event. ++ * An extended time_stamp is a 64-bit timestamp represented ++ * internally in a special way that makes the best use of space ++ * contained within a ring buffer event. This function decodes ++ * it and maps it to a straight u64 value. ++ */ ++u64 ring_buffer_event_time_stamp(struct ring_buffer_event *event) ++{ ++ u64 ts; ++ ++ ts = event->array[0]; ++ ts <<= TS_SHIFT; ++ ts += event->time_delta; ++ ++ return ts; ++} ++ + /* 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 + } + + /* Slow path, do not inline */ +-static noinline struct ring_buffer_event * +-rb_add_time_stamp(struct ring_buffer_event *event, u64 delta) ++static struct noinline ring_buffer_event * ++rb_add_time_stamp(struct ring_buffer_event *event, u64 delta, bool abs) + { +- event->type_len = RINGBUF_TYPE_TIME_EXTEND; ++ if (abs) ++ event->type_len = RINGBUF_TYPE_TIME_STAMP; ++ else ++ event->type_len = RINGBUF_TYPE_TIME_EXTEND; + +- /* Not the first event on the page? */ +- if (rb_event_index(event)) { ++ /* Not the first event on the page, or not delta? */ ++ if (abs || rb_event_index(event)) { + 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 + * add it to the start of the resevered space. + */ + if (unlikely(info->add_timestamp)) { +- event = rb_add_time_stamp(event, delta); ++ bool abs = ring_buffer_time_stamp_abs(cpu_buffer->buffer); ++ ++ event = rb_add_time_stamp(event, info->delta, abs); + length -= RB_LEN_TIME_EXTEND; + delta = 0; + } +@@ -2456,7 +2485,7 @@ static __always_inline void rb_end_commi + + static inline void rb_event_discard(struct ring_buffer_event *event) + { +- if (event->type_len == RINGBUF_TYPE_TIME_EXTEND) ++ if (extended_time(event)) + 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 + { + u64 delta; + ++ /* In TIME_STAMP mode, write_stamp is unused, nothing to do */ ++ if (event->type_len == RINGBUF_TYPE_TIME_STAMP) ++ return; ++ + /* + * The event first in the commit queue updates the + * time stamp. +@@ -2500,9 +2533,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) { +- delta = event->array[0]; +- delta <<= TS_SHIFT; +- delta += event->time_delta; ++ delta = ring_buffer_event_time_stamp(event); + cpu_buffer->write_stamp += delta; + } else + cpu_buffer->write_stamp += event->time_delta; +@@ -2686,7 +2717,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. + */ +- if (!tail) ++ if (!tail && !ring_buffer_time_stamp_abs(cpu_buffer->buffer)) + 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 + /* make sure this diff is calculated here */ + barrier(); + +- /* Did the write stamp get updated already? */ +- if (likely(info.ts >= cpu_buffer->write_stamp)) { ++ if (ring_buffer_time_stamp_abs(buffer)) { ++ info.delta = info.ts; ++ rb_handle_timestamp(cpu_buffer, &info); ++ } else /* Did the write stamp get updated already? */ ++ if (likely(info.ts >= cpu_buffer->write_stamp)) { + 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_ + return; + + case RINGBUF_TYPE_TIME_EXTEND: +- delta = event->array[0]; +- delta <<= TS_SHIFT; +- delta += event->time_delta; ++ delta = ring_buffer_event_time_stamp(event); + cpu_buffer->read_stamp += delta; + return; + + case RINGBUF_TYPE_TIME_STAMP: +- /* FIXME: not implemented */ ++ /* In TIME_STAMP mode, write_stamp is unused, nothing to do */ + return; + + case RINGBUF_TYPE_DATA: +@@ -3478,14 +3510,12 @@ rb_update_iter_read_stamp(struct ring_bu + return; + + case RINGBUF_TYPE_TIME_EXTEND: +- delta = event->array[0]; +- delta <<= TS_SHIFT; +- delta += event->time_delta; ++ delta = ring_buffer_event_time_stamp(event); + iter->read_stamp += delta; + return; + + case RINGBUF_TYPE_TIME_STAMP: +- /* FIXME: not implemented */ ++ /* In TIME_STAMP mode, write_stamp is unused, nothing to do */ + return; + + case RINGBUF_TYPE_DATA: +@@ -3709,6 +3739,8 @@ rb_buffer_peek(struct ring_buffer_per_cp + struct buffer_page *reader; + int nr_loops = 0; + ++ if (ts) ++ *ts = 0; + again: + /* + * We repeat when a time extend is encountered. +@@ -3745,12 +3777,17 @@ rb_buffer_peek(struct ring_buffer_per_cp + goto again; + + case RINGBUF_TYPE_TIME_STAMP: +- /* FIXME: not implemented */ ++ if (ts) { ++ *ts = ring_buffer_event_time_stamp(event); ++ ring_buffer_normalize_time_stamp(cpu_buffer->buffer, ++ cpu_buffer->cpu, ts); ++ } ++ /* Internal data, OK to advance */ + rb_advance_reader(cpu_buffer); + goto again; + + case RINGBUF_TYPE_DATA: +- if (ts) { ++ if (ts && !(*ts)) { + *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 + struct ring_buffer_event *event; + int nr_loops = 0; + ++ if (ts) ++ *ts = 0; ++ + cpu_buffer = iter->cpu_buffer; + buffer = cpu_buffer->buffer; + +@@ -3827,12 +3867,17 @@ rb_iter_peek(struct ring_buffer_iter *it + goto again; + + case RINGBUF_TYPE_TIME_STAMP: +- /* FIXME: not implemented */ ++ if (ts) { ++ *ts = ring_buffer_event_time_stamp(event); ++ ring_buffer_normalize_time_stamp(cpu_buffer->buffer, ++ cpu_buffer->cpu, ts); ++ } ++ /* Internal data, OK to advance */ + rb_advance_iter(iter); + goto again; + + case RINGBUF_TYPE_DATA: +- if (ts) { ++ if (ts && !(*ts)) { + *ts = iter->read_stamp + event->time_delta; + ring_buffer_normalize_time_stamp(buffer, + cpu_buffer->cpu, ts); diff --git a/patches/0004-rtmutex-Clean-up.patch b/patches/0004-rtmutex-Clean-up.patch index b3f244c9879eb..7140b519190ef 100644 --- a/patches/0004-rtmutex-Clean-up.patch +++ b/patches/0004-rtmutex-Clean-up.patch @@ -1,4 +1,3 @@ -From aa2bfe55366552cb7e93e8709d66e698d79ccc47 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra <peterz@infradead.org> Date: Thu, 23 Mar 2017 15:56:10 +0100 Subject: [PATCH 4/9] rtmutex: Clean up diff --git a/patches/0004-x86-smp-Adjust-system_state-check.patch b/patches/0004-x86-smp-Adjust-system_state-check.patch index eadb43fddb245..98ac774fa5b51 100644 --- a/patches/0004-x86-smp-Adjust-system_state-check.patch +++ b/patches/0004-x86-smp-Adjust-system_state-check.patch @@ -1,4 +1,3 @@ -From 719b3680d1f789c1e3054e3fcb26bfff07c3c623 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:35 +0200 Subject: [PATCH 04/17] x86/smp: Adjust system_state check diff --git a/patches/0005-metag-Adjust-system_state-check.patch b/patches/0005-metag-Adjust-system_state-check.patch index 61fd1559307c9..3a2cc0d8897b6 100644 --- a/patches/0005-metag-Adjust-system_state-check.patch +++ b/patches/0005-metag-Adjust-system_state-check.patch @@ -1,4 +1,3 @@ -From dcd2e4734b428709984e2fa35ebbd6cccc246d47 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:36 +0200 Subject: [PATCH 05/17] metag: Adjust system_state check diff --git a/patches/0005-powerpc-smp-Replace-open-coded-task-affinity-logic.patch b/patches/0005-powerpc-smp-Replace-open-coded-task-affinity-logic.patch index a6d685b80effc..3fa118c442469 100644 --- a/patches/0005-powerpc-smp-Replace-open-coded-task-affinity-logic.patch +++ b/patches/0005-powerpc-smp-Replace-open-coded-task-affinity-logic.patch @@ -1,4 +1,3 @@ -From 6d11b87d55eb75007a3721c2de5938f5bbf607fb Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 12 Apr 2017 22:07:31 +0200 Subject: [PATCH 05/13] powerpc/smp: Replace open coded task affinity logic diff --git a/patches/0005-sched-rtmutex-Refactor-rt_mutex_setprio.patch b/patches/0005-sched-rtmutex-Refactor-rt_mutex_setprio.patch index 03f34c2b6a2fe..54ccf522b2aca 100644 --- a/patches/0005-sched-rtmutex-Refactor-rt_mutex_setprio.patch +++ b/patches/0005-sched-rtmutex-Refactor-rt_mutex_setprio.patch @@ -1,4 +1,3 @@ -From acd58620e415aee4a43a808d7d2fd87259ee0001 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra <peterz@infradead.org> Date: Thu, 23 Mar 2017 15:56:11 +0100 Subject: [PATCH 5/9] sched/rtmutex: Refactor rt_mutex_setprio() diff --git a/patches/0005-stop_machine-Provide-stop_machine_cpuslocked.patch b/patches/0005-stop_machine-Provide-stop_machine_cpuslocked.patch index 8e5d24bebe43d..d1dd0d8a5081f 100644 --- a/patches/0005-stop_machine-Provide-stop_machine_cpuslocked.patch +++ b/patches/0005-stop_machine-Provide-stop_machine_cpuslocked.patch @@ -1,4 +1,3 @@ -From fe5595c074005bd94f0c7d1644175941149f6768 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Wed, 24 May 2017 10:15:16 +0200 Subject: [PATCH 05/32] stop_machine: Provide stop_machine_cpuslocked() diff --git a/patches/0005-tracing-Give-event-triggers-access-to-ring_buffer_ev.patch b/patches/0005-tracing-Give-event-triggers-access-to-ring_buffer_ev.patch new file mode 100644 index 0000000000000..5f55d10387c06 --- /dev/null +++ b/patches/0005-tracing-Give-event-triggers-access-to-ring_buffer_ev.patch @@ -0,0 +1,298 @@ +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 + ring_buffer_event + +The ring_buffer event can provide a timestamp that may be useful to +various triggers - pass it into the handlers for that purpose. + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + include/linux/trace_events.h | 14 ++++++---- + kernel/trace/trace.h | 9 +++--- + kernel/trace/trace_events_hist.c | 11 +++++--- + kernel/trace/trace_events_trigger.c | 47 ++++++++++++++++++++++-------------- + 4 files changed, 49 insertions(+), 32 deletions(-) + +--- a/include/linux/trace_events.h ++++ b/include/linux/trace_events.h +@@ -400,11 +400,13 @@ enum event_trigger_type { + + extern int filter_match_preds(struct event_filter *filter, void *rec); + +-extern enum event_trigger_type event_triggers_call(struct trace_event_file *file, +- void *rec); +-extern void event_triggers_post_call(struct trace_event_file *file, +- enum event_trigger_type tt, +- void *rec); ++extern enum event_trigger_type ++event_triggers_call(struct trace_event_file *file, void *rec, ++ struct ring_buffer_event *event); ++extern void ++event_triggers_post_call(struct trace_event_file *file, ++ enum event_trigger_type tt, ++ void *rec, struct ring_buffer_event *event); + + bool trace_event_ignore_this_pid(struct trace_event_file *trace_file); + +@@ -424,7 +426,7 @@ trace_trigger_soft_disabled(struct trace + + if (!(eflags & EVENT_FILE_FL_TRIGGER_COND)) { + if (eflags & EVENT_FILE_FL_TRIGGER_MODE) +- event_triggers_call(file, NULL); ++ event_triggers_call(file, NULL, NULL); + if (eflags & EVENT_FILE_FL_SOFT_DISABLED) + return true; + if (eflags & EVENT_FILE_FL_PID_FILTER) +--- a/kernel/trace/trace.h ++++ b/kernel/trace/trace.h +@@ -1189,7 +1189,7 @@ static inline bool + unsigned long eflags = file->flags; + + if (eflags & EVENT_FILE_FL_TRIGGER_COND) +- *tt = event_triggers_call(file, entry); ++ *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) && +@@ -1226,7 +1226,7 @@ event_trigger_unlock_commit(struct trace + trace_buffer_unlock_commit(file->tr, buffer, event, irq_flags, pc); + + if (tt) +- event_triggers_post_call(file, tt, entry); ++ event_triggers_post_call(file, tt, entry, event); + } + + /** +@@ -1259,7 +1259,7 @@ event_trigger_unlock_commit_regs(struct + irq_flags, pc, regs); + + if (tt) +- event_triggers_post_call(file, tt, entry); ++ event_triggers_post_call(file, tt, entry, event); + } + + #define FILTER_PRED_INVALID ((unsigned short)-1) +@@ -1482,7 +1482,8 @@ extern int register_trigger_hist_enable_ + */ + struct event_trigger_ops { + void (*func)(struct event_trigger_data *data, +- void *rec); ++ void *rec, ++ struct ring_buffer_event *rbe); + int (*init)(struct event_trigger_ops *ops, + struct event_trigger_data *data); + 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 + memcpy(compound_key + key_field->offset, key, size); + } + +-static void event_hist_trigger(struct event_trigger_data *data, void *rec) ++static void event_hist_trigger(struct event_trigger_data *data, void *rec, ++ struct ring_buffer_event *event) + { + 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 + } + + static void +-hist_enable_trigger(struct event_trigger_data *data, void *rec) ++hist_enable_trigger(struct event_trigger_data *data, void *rec, ++ struct ring_buffer_event *event) + { + struct enable_trigger_data *enable_data = data->private_data; + struct event_trigger_data *test; +@@ -1688,7 +1690,8 @@ hist_enable_trigger(struct event_trigger + } + + static void +-hist_enable_count_trigger(struct event_trigger_data *data, void *rec) ++hist_enable_count_trigger(struct event_trigger_data *data, void *rec, ++ struct ring_buffer_event *event) + { + if (!data->count) + return; +@@ -1696,7 +1699,7 @@ hist_enable_count_trigger(struct event_t + if (data->count != -1) + (data->count)--; + +- hist_enable_trigger(data, rec); ++ hist_enable_trigger(data, rec, event); + } + + static struct event_trigger_ops hist_enable_trigger_ops = { +--- a/kernel/trace/trace_events_trigger.c ++++ b/kernel/trace/trace_events_trigger.c +@@ -63,7 +63,8 @@ void trigger_data_free(struct event_trig + * any trigger that should be deferred, ETT_NONE if nothing to defer. + */ + enum event_trigger_type +-event_triggers_call(struct trace_event_file *file, void *rec) ++event_triggers_call(struct trace_event_file *file, void *rec, ++ struct ring_buffer_event *event) + { + struct event_trigger_data *data; + enum event_trigger_type tt = ETT_NONE; +@@ -76,7 +77,7 @@ event_triggers_call(struct trace_event_f + if (data->paused) + continue; + if (!rec) { +- data->ops->func(data, rec); ++ data->ops->func(data, rec, event); + continue; + } + filter = rcu_dereference_sched(data->filter); +@@ -86,7 +87,7 @@ event_triggers_call(struct trace_event_f + tt |= data->cmd_ops->trigger_type; + continue; + } +- data->ops->func(data, rec); ++ data->ops->func(data, rec, event); + } + return tt; + } +@@ -108,7 +109,7 @@ EXPORT_SYMBOL_GPL(event_triggers_call); + void + event_triggers_post_call(struct trace_event_file *file, + enum event_trigger_type tt, +- void *rec) ++ void *rec, struct ring_buffer_event *event) + { + struct event_trigger_data *data; + +@@ -116,7 +117,7 @@ event_triggers_post_call(struct trace_ev + if (data->paused) + continue; + if (data->cmd_ops->trigger_type & tt) +- data->ops->func(data, rec); ++ data->ops->func(data, rec, event); + } + } + EXPORT_SYMBOL_GPL(event_triggers_post_call); +@@ -909,7 +910,8 @@ void set_named_trigger_data(struct event + } + + static void +-traceon_trigger(struct event_trigger_data *data, void *rec) ++traceon_trigger(struct event_trigger_data *data, void *rec, ++ struct ring_buffer_event *event) + { + if (tracing_is_on()) + return; +@@ -918,7 +920,8 @@ traceon_trigger(struct event_trigger_dat + } + + static void +-traceon_count_trigger(struct event_trigger_data *data, void *rec) ++traceon_count_trigger(struct event_trigger_data *data, void *rec, ++ struct ring_buffer_event *event) + { + if (tracing_is_on()) + return; +@@ -933,7 +936,8 @@ traceon_count_trigger(struct event_trigg + } + + static void +-traceoff_trigger(struct event_trigger_data *data, void *rec) ++traceoff_trigger(struct event_trigger_data *data, void *rec, ++ struct ring_buffer_event *event) + { + if (!tracing_is_on()) + return; +@@ -942,7 +946,8 @@ traceoff_trigger(struct event_trigger_da + } + + static void +-traceoff_count_trigger(struct event_trigger_data *data, void *rec) ++traceoff_count_trigger(struct event_trigger_data *data, void *rec, ++ struct ring_buffer_event *event) + { + if (!tracing_is_on()) + return; +@@ -1039,13 +1044,15 @@ static struct event_command trigger_trac + + #ifdef CONFIG_TRACER_SNAPSHOT + static void +-snapshot_trigger(struct event_trigger_data *data, void *rec) ++snapshot_trigger(struct event_trigger_data *data, void *rec, ++ struct ring_buffer_event *event) + { + tracing_snapshot(); + } + + static void +-snapshot_count_trigger(struct event_trigger_data *data, void *rec) ++snapshot_count_trigger(struct event_trigger_data *data, void *rec, ++ struct ring_buffer_event *event) + { + if (!data->count) + return; +@@ -1053,7 +1060,7 @@ snapshot_count_trigger(struct event_trig + if (data->count != -1) + (data->count)--; + +- snapshot_trigger(data, rec); ++ snapshot_trigger(data, rec, event); + } + + static int +@@ -1132,13 +1139,15 @@ static __init int register_trigger_snaps + #define STACK_SKIP 3 + + static void +-stacktrace_trigger(struct event_trigger_data *data, void *rec) ++stacktrace_trigger(struct event_trigger_data *data, void *rec, ++ struct ring_buffer_event *event) + { + trace_dump_stack(STACK_SKIP); + } + + static void +-stacktrace_count_trigger(struct event_trigger_data *data, void *rec) ++stacktrace_count_trigger(struct event_trigger_data *data, void *rec, ++ struct ring_buffer_event *event) + { + if (!data->count) + return; +@@ -1146,7 +1155,7 @@ stacktrace_count_trigger(struct event_tr + if (data->count != -1) + (data->count)--; + +- stacktrace_trigger(data, rec); ++ stacktrace_trigger(data, rec, event); + } + + static int +@@ -1208,7 +1217,8 @@ static __init void unregister_trigger_tr + } + + static void +-event_enable_trigger(struct event_trigger_data *data, void *rec) ++event_enable_trigger(struct event_trigger_data *data, void *rec, ++ struct ring_buffer_event *event) + { + struct enable_trigger_data *enable_data = data->private_data; + +@@ -1219,7 +1229,8 @@ event_enable_trigger(struct event_trigge + } + + static void +-event_enable_count_trigger(struct event_trigger_data *data, void *rec) ++event_enable_count_trigger(struct event_trigger_data *data, void *rec, ++ struct ring_buffer_event *event) + { + struct enable_trigger_data *enable_data = data->private_data; + +@@ -1233,7 +1244,7 @@ event_enable_count_trigger(struct event_ + if (data->count != -1) + (data->count)--; + +- event_enable_trigger(data, rec); ++ event_enable_trigger(data, rec, event); + } + + int event_enable_trigger_print(struct seq_file *m, diff --git a/patches/0006-padata-Make-padata_alloc-static.patch b/patches/0006-padata-Make-padata_alloc-static.patch index 2ffdd6f8f55e6..7c79b20c0622c 100644 --- a/patches/0006-padata-Make-padata_alloc-static.patch +++ b/patches/0006-padata-Make-padata_alloc-static.patch @@ -1,4 +1,3 @@ -From 9596695ee1e7eedd743c43811fe68299eb005b5c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:17 +0200 Subject: [PATCH 06/32] padata: Make padata_alloc() static diff --git a/patches/0006-powerpc-Adjust-system_state-check.patch b/patches/0006-powerpc-Adjust-system_state-check.patch index 9e668c663c3a9..5250cfaadcb93 100644 --- a/patches/0006-powerpc-Adjust-system_state-check.patch +++ b/patches/0006-powerpc-Adjust-system_state-check.patch @@ -1,4 +1,3 @@ -From a8fcfc1917681ba1ccc23a429543a67aad8bfd00 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:37 +0200 Subject: [PATCH 06/17] powerpc: Adjust system_state check diff --git a/patches/0006-sched-tracing-Update-trace_sched_pi_setprio.patch b/patches/0006-sched-tracing-Update-trace_sched_pi_setprio.patch index 8a6ea453a154a..e1918397abb4c 100644 --- a/patches/0006-sched-tracing-Update-trace_sched_pi_setprio.patch +++ b/patches/0006-sched-tracing-Update-trace_sched_pi_setprio.patch @@ -1,4 +1,3 @@ -From b91473ff6e979c0028f02f90e40c844959c736d8 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra <peterz@infradead.org> Date: Thu, 23 Mar 2017 15:56:12 +0100 Subject: [PATCH 6/9] sched,tracing: Update trace_sched_pi_setprio() diff --git a/patches/0006-sparc-sysfs-Replace-racy-task-affinity-logic.patch b/patches/0006-sparc-sysfs-Replace-racy-task-affinity-logic.patch index 9e3bde0280f6f..af399790e7cc1 100644 --- a/patches/0006-sparc-sysfs-Replace-racy-task-affinity-logic.patch +++ b/patches/0006-sparc-sysfs-Replace-racy-task-affinity-logic.patch @@ -1,4 +1,3 @@ -From ea875ec94eafb858990f3fe9528501f983105653 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Thu, 13 Apr 2017 10:17:07 +0200 Subject: [PATCH 06/13] sparc/sysfs: Replace racy task affinity logic diff --git a/patches/0006-tracing-Add-ring-buffer-event-param-to-hist-field-fu.patch b/patches/0006-tracing-Add-ring-buffer-event-param-to-hist-field-fu.patch new file mode 100644 index 0000000000000..9d2ac71a19282 --- /dev/null +++ b/patches/0006-tracing-Add-ring-buffer-event-param-to-hist-field-fu.patch @@ -0,0 +1,139 @@ +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 + functions + +Some events such as timestamps require access to a ring_buffer_event +struct; add a param so that hist field functions can access 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 | 39 ++++++++++++++++++++++++--------------- + 1 file changed, 24 insertions(+), 15 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -26,7 +26,8 @@ + + struct hist_field; + +-typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event); ++typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event, ++ struct ring_buffer_event *rbe); + + #define HIST_FIELD_OPERANDS_MAX 2 + +@@ -40,24 +41,28 @@ struct hist_field { + struct hist_field *operands[HIST_FIELD_OPERANDS_MAX]; + }; + +-static u64 hist_field_none(struct hist_field *field, void *event) ++static u64 hist_field_none(struct hist_field *field, void *event, ++ struct ring_buffer_event *rbe) + { + return 0; + } + +-static u64 hist_field_counter(struct hist_field *field, void *event) ++static u64 hist_field_counter(struct hist_field *field, void *event, ++ struct ring_buffer_event *rbe) + { + return 1; + } + +-static u64 hist_field_string(struct hist_field *hist_field, void *event) ++static u64 hist_field_string(struct hist_field *hist_field, void *event, ++ struct ring_buffer_event *rbe) + { + 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) ++static u64 hist_field_dynstring(struct hist_field *hist_field, void *event, ++ struct ring_buffer_event *rbe) + { + u32 str_item = *(u32 *)(event + hist_field->field->offset); + int str_loc = str_item & 0xffff; +@@ -66,24 +71,28 @@ static u64 hist_field_dynstring(struct h + return (u64)(unsigned long)addr; + } + +-static u64 hist_field_pstring(struct hist_field *hist_field, void *event) ++static u64 hist_field_pstring(struct hist_field *hist_field, void *event, ++ struct ring_buffer_event *rbe) + { + 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) ++static u64 hist_field_log2(struct hist_field *hist_field, void *event, ++ struct ring_buffer_event *rbe) + { + struct hist_field *operand = hist_field->operands[0]; + +- u64 val = operand->fn(operand, event); ++ u64 val = operand->fn(operand, event, rbe); + + return (u64) ilog2(roundup_pow_of_two(val)); + } + + #define DEFINE_HIST_FIELD_FN(type) \ +-static u64 hist_field_##type(struct hist_field *hist_field, void *event)\ ++ static u64 hist_field_##type(struct hist_field *hist_field, \ ++ void *event, \ ++ struct ring_buffer_event *rbe) \ + { \ + type *addr = (type *)(event + hist_field->field->offset); \ + \ +@@ -883,8 +892,8 @@ 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) ++ struct tracing_map_elt *elt, void *rec, ++ struct ring_buffer_event *rbe) + { + struct hist_field *hist_field; + unsigned int i; +@@ -892,7 +901,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, rec); ++ hist_val = hist_field->fn(hist_field, rec, rbe); + tracing_map_update_sum(elt, i, hist_val); + } + } +@@ -922,7 +931,7 @@ static inline void add_to_key(char *comp + } + + static void event_hist_trigger(struct event_trigger_data *data, void *rec, +- struct ring_buffer_event *event) ++ struct ring_buffer_event *rbe) + { + 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 + + key = entries; + } else { +- field_contents = key_field->fn(key_field, rec); ++ field_contents = key_field->fn(key_field, rec, rbe); + 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 + + elt = tracing_map_insert(hist_data->map, key); + if (elt) +- hist_trigger_elt_update(hist_data, elt, rec); ++ hist_trigger_elt_update(hist_data, elt, rec, rbe); + } + + static void hist_trigger_stacktrace_print(struct seq_file *m, diff --git a/patches/0007-ACPI-Adjust-system_state-check.patch b/patches/0007-ACPI-Adjust-system_state-check.patch index 6859f34757a3e..d9445d1e81992 100644 --- a/patches/0007-ACPI-Adjust-system_state-check.patch +++ b/patches/0007-ACPI-Adjust-system_state-check.patch @@ -1,4 +1,3 @@ -From 9762b33dc31c67e34b36ba4e787e64084b3136ff Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:38 +0200 Subject: [PATCH 07/17] ACPI: Adjust system_state check diff --git a/patches/0007-ACPI-processor-Fix-error-handling-in-__acpi_processo.patch b/patches/0007-ACPI-processor-Fix-error-handling-in-__acpi_processo.patch index 0e155d91d008c..01de1e487cfa9 100644 --- a/patches/0007-ACPI-processor-Fix-error-handling-in-__acpi_processo.patch +++ b/patches/0007-ACPI-processor-Fix-error-handling-in-__acpi_processo.patch @@ -1,4 +1,3 @@ -From a5cbdf693a60d5b86d4d21dfedd90f17754eb273 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 12 Apr 2017 22:07:33 +0200 Subject: [PATCH 07/13] ACPI/processor: Fix error handling in diff --git a/patches/0007-padata-Avoid-nested-calls-to-cpus_read_lock-in-pcryp.patch b/patches/0007-padata-Avoid-nested-calls-to-cpus_read_lock-in-pcryp.patch index a0b5d93465a6d..9929deb90bd58 100644 --- a/patches/0007-padata-Avoid-nested-calls-to-cpus_read_lock-in-pcryp.patch +++ b/patches/0007-padata-Avoid-nested-calls-to-cpus_read_lock-in-pcryp.patch @@ -1,4 +1,3 @@ -From c5a81c8ff816d89941fe86961b286765d6ca2f5f Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Wed, 24 May 2017 10:15:18 +0200 Subject: [PATCH 07/32] padata: Avoid nested calls to cpus_read_lock() in diff --git a/patches/0007-rtmutex-Fix-PI-chain-order-integrity.patch b/patches/0007-rtmutex-Fix-PI-chain-order-integrity.patch index 4514f5ee65dfd..62ae9e900947f 100644 --- a/patches/0007-rtmutex-Fix-PI-chain-order-integrity.patch +++ b/patches/0007-rtmutex-Fix-PI-chain-order-integrity.patch @@ -1,4 +1,3 @@ -From e0aad5b44ff5d28ac1d6ae70cdf84ca228e889dc Mon Sep 17 00:00:00 2001 From: Peter Zijlstra <peterz@infradead.org> Date: Thu, 23 Mar 2017 15:56:13 +0100 Subject: [PATCH 7/9] rtmutex: Fix PI chain order integrity diff --git a/patches/0007-tracing-Increase-tracing-map-KEYS_MAX-size.patch b/patches/0007-tracing-Increase-tracing-map-KEYS_MAX-size.patch new file mode 100644 index 0000000000000..f889dff5f4845 --- /dev/null +++ b/patches/0007-tracing-Increase-tracing-map-KEYS_MAX-size.patch @@ -0,0 +1,24 @@ +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 + +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. + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/tracing_map.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/kernel/trace/tracing_map.h ++++ b/kernel/trace/tracing_map.h +@@ -5,7 +5,7 @@ + #define TRACING_MAP_BITS_MAX 17 + #define TRACING_MAP_BITS_MIN 7 + +-#define TRACING_MAP_KEYS_MAX 2 ++#define TRACING_MAP_KEYS_MAX 3 + #define TRACING_MAP_VALS_MAX 3 + #define TRACING_MAP_FIELDS_MAX (TRACING_MAP_KEYS_MAX + \ + TRACING_MAP_VALS_MAX) diff --git a/patches/0008-ACPI-processor-Replace-racy-task-affinity-logic.patch b/patches/0008-ACPI-processor-Replace-racy-task-affinity-logic.patch index e793161acc518..485477ee3f7d5 100644 --- a/patches/0008-ACPI-processor-Replace-racy-task-affinity-logic.patch +++ b/patches/0008-ACPI-processor-Replace-racy-task-affinity-logic.patch @@ -1,4 +1,3 @@ -From 8153f9ac43897f9f4786b30badc134fcc1a4fb11 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 12 Apr 2017 22:07:34 +0200 Subject: [PATCH 08/13] ACPI/processor: Replace racy task affinity logic diff --git a/patches/0008-mm-Adjust-system_state-check.patch b/patches/0008-mm-Adjust-system_state-check.patch index bc80d72e2112a..978c025e00a66 100644 --- a/patches/0008-mm-Adjust-system_state-check.patch +++ b/patches/0008-mm-Adjust-system_state-check.patch @@ -1,4 +1,3 @@ -From 8cdde385c7a33afbe13fd71351da0968540fa566 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:39 +0200 Subject: [PATCH 08/17] mm: Adjust system_state check diff --git a/patches/0008-rtmutex-Fix-more-prio-comparisons.patch b/patches/0008-rtmutex-Fix-more-prio-comparisons.patch index d8e690b4742bc..e2ea32a6ce215 100644 --- a/patches/0008-rtmutex-Fix-more-prio-comparisons.patch +++ b/patches/0008-rtmutex-Fix-more-prio-comparisons.patch @@ -1,4 +1,3 @@ -From 19830e55247cddb3f46f1bf60b8e245593491bea Mon Sep 17 00:00:00 2001 From: Peter Zijlstra <peterz@infradead.org> Date: Thu, 23 Mar 2017 15:56:14 +0100 Subject: [PATCH 8/9] rtmutex: Fix more prio comparisons diff --git a/patches/0008-tracing-Break-out-hist-trigger-assignment-parsing.patch b/patches/0008-tracing-Break-out-hist-trigger-assignment-parsing.patch new file mode 100644 index 0000000000000..07d771d47db69 --- /dev/null +++ b/patches/0008-tracing-Break-out-hist-trigger-assignment-parsing.patch @@ -0,0 +1,91 @@ +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 + +This will make it easier to add variables, and makes the parsing code +cleaner regardless. + +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 | 56 ++++++++++++++++++++++++--------------- + 1 file changed, 35 insertions(+), 21 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -251,6 +251,35 @@ static void destroy_hist_trigger_attrs(s + kfree(attrs); + } + ++static int parse_assignment(char *str, struct hist_trigger_attrs *attrs) ++{ ++ int ret = 0; ++ ++ if ((strncmp(str, "key=", strlen("key=")) == 0) || ++ (strncmp(str, "keys=", strlen("keys=")) == 0)) ++ attrs->keys_str = kstrdup(str, GFP_KERNEL); ++ else if ((strncmp(str, "val=", strlen("val=")) == 0) || ++ (strncmp(str, "vals=", strlen("vals=")) == 0) || ++ (strncmp(str, "values=", strlen("values=")) == 0)) ++ attrs->vals_str = kstrdup(str, GFP_KERNEL); ++ else if (strncmp(str, "sort=", strlen("sort=")) == 0) ++ attrs->sort_key_str = kstrdup(str, GFP_KERNEL); ++ else if (strncmp(str, "name=", strlen("name=")) == 0) ++ attrs->name = kstrdup(str, GFP_KERNEL); ++ else if (strncmp(str, "size=", strlen("size=")) == 0) { ++ int map_bits = parse_map_size(str); ++ ++ if (map_bits < 0) { ++ ret = map_bits; ++ goto out; ++ } ++ attrs->map_bits = map_bits; ++ } else ++ ret = -EINVAL; ++ out: ++ return ret; ++} ++ + static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str) + { + struct hist_trigger_attrs *attrs; +@@ -263,33 +292,18 @@ static struct hist_trigger_attrs *parse_ + while (trigger_str) { + char *str = strsep(&trigger_str, ":"); + +- if ((strncmp(str, "key=", strlen("key=")) == 0) || +- (strncmp(str, "keys=", strlen("keys=")) == 0)) +- attrs->keys_str = kstrdup(str, GFP_KERNEL); +- else if ((strncmp(str, "val=", strlen("val=")) == 0) || +- (strncmp(str, "vals=", strlen("vals=")) == 0) || +- (strncmp(str, "values=", strlen("values=")) == 0)) +- attrs->vals_str = kstrdup(str, GFP_KERNEL); +- else if (strncmp(str, "sort=", strlen("sort=")) == 0) +- attrs->sort_key_str = kstrdup(str, GFP_KERNEL); +- else if (strncmp(str, "name=", strlen("name=")) == 0) +- attrs->name = kstrdup(str, GFP_KERNEL); +- else if (strcmp(str, "pause") == 0) ++ if (strchr(str, '=')) { ++ ret = parse_assignment(str, attrs); ++ if (ret) ++ goto free; ++ } else if (strcmp(str, "pause") == 0) + attrs->pause = true; + else if ((strcmp(str, "cont") == 0) || + (strcmp(str, "continue") == 0)) + attrs->cont = true; + else if (strcmp(str, "clear") == 0) + attrs->clear = true; +- else if (strncmp(str, "size=", strlen("size=")) == 0) { +- int map_bits = parse_map_size(str); +- +- if (map_bits < 0) { +- ret = map_bits; +- goto free; +- } +- attrs->map_bits = map_bits; +- } else { ++ else { + ret = -EINVAL; + goto free; + } diff --git a/patches/0008-x86-mtrr-Remove-get_online_cpus-from-mtrr_save_state.patch b/patches/0008-x86-mtrr-Remove-get_online_cpus-from-mtrr_save_state.patch index 60c81a794fdad..01ad8962113fa 100644 --- a/patches/0008-x86-mtrr-Remove-get_online_cpus-from-mtrr_save_state.patch +++ b/patches/0008-x86-mtrr-Remove-get_online_cpus-from-mtrr_save_state.patch @@ -1,4 +1,3 @@ -From 547efeadd42a3c75e41e33c0637cba100fc18289 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Wed, 24 May 2017 10:15:19 +0200 Subject: [PATCH 08/32] x86/mtrr: Remove get_online_cpus() from diff --git a/patches/0009-cpufreq-Use-cpuhp_setup_state_nocalls_cpuslocked.patch b/patches/0009-cpufreq-Use-cpuhp_setup_state_nocalls_cpuslocked.patch index cdda01224aeca..f45df3bddaef1 100644 --- a/patches/0009-cpufreq-Use-cpuhp_setup_state_nocalls_cpuslocked.patch +++ b/patches/0009-cpufreq-Use-cpuhp_setup_state_nocalls_cpuslocked.patch @@ -1,4 +1,3 @@ -From a92551e41d5a7b563ae440496bc5ca19d205231d Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Wed, 24 May 2017 10:15:20 +0200 Subject: [PATCH 09/32] cpufreq: Use cpuhp_setup_state_nocalls_cpuslocked() diff --git a/patches/0009-cpufreq-ia64-Replace-racy-task-affinity-logic.patch b/patches/0009-cpufreq-ia64-Replace-racy-task-affinity-logic.patch index c396649cc1b0b..5cdf89a318f34 100644 --- a/patches/0009-cpufreq-ia64-Replace-racy-task-affinity-logic.patch +++ b/patches/0009-cpufreq-ia64-Replace-racy-task-affinity-logic.patch @@ -1,4 +1,3 @@ -From 38f05ed04beb276f780fcd2b5c0b78c76d0b3c0c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 12 Apr 2017 22:55:03 +0200 Subject: [PATCH 09/13] cpufreq/ia64: Replace racy task affinity logic diff --git a/patches/0009-cpufreq-pasemi-Adjust-system_state-check.patch b/patches/0009-cpufreq-pasemi-Adjust-system_state-check.patch index 8085857d07be3..bdcab4ad03e1f 100644 --- a/patches/0009-cpufreq-pasemi-Adjust-system_state-check.patch +++ b/patches/0009-cpufreq-pasemi-Adjust-system_state-check.patch @@ -1,4 +1,3 @@ -From d04e31a23c3c828456cb5613f391ce4ac4e5765f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:40 +0200 Subject: [PATCH 09/17] cpufreq/pasemi: Adjust system_state check diff --git a/patches/0009-rtmutex-Plug-preempt-count-leak-in-rt_mutex_futex_un.patch b/patches/0009-rtmutex-Plug-preempt-count-leak-in-rt_mutex_futex_un.patch index bd28dcbafb94c..a9dd7e64add56 100644 --- a/patches/0009-rtmutex-Plug-preempt-count-leak-in-rt_mutex_futex_un.patch +++ b/patches/0009-rtmutex-Plug-preempt-count-leak-in-rt_mutex_futex_un.patch @@ -1,4 +1,3 @@ -From def34eaae5ce04b324e48e1bfac873091d945213 Mon Sep 17 00:00:00 2001 From: Mike Galbraith <efault@gmx.de> Date: Wed, 5 Apr 2017 10:08:27 +0200 Subject: [PATCH 9/9] rtmutex: Plug preempt count leak in diff --git a/patches/0009-tracing-Make-traceprobe-parsing-code-reusable.patch b/patches/0009-tracing-Make-traceprobe-parsing-code-reusable.patch new file mode 100644 index 0000000000000..3ae6fd7b257ba --- /dev/null +++ b/patches/0009-tracing-Make-traceprobe-parsing-code-reusable.patch @@ -0,0 +1,317 @@ +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 + +traceprobe_probes_write() and traceprobe_command() actually contain +nothing that ties them to kprobes - the code is generically useful for +similar types of parsing elsewhere, so separate it out and move it to +trace.c/trace.h. + +Other than moving it, the only change is in naming: +traceprobe_probes_write() becomes trace_parse_run_command() and +traceprobe_command() becomes trace_run_command(). + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/trace.c | 86 ++++++++++++++++++++++++++++++++++++++++++++ + kernel/trace/trace.h | 7 +++ + kernel/trace/trace_kprobe.c | 18 ++++----- + kernel/trace/trace_probe.c | 86 -------------------------------------------- + kernel/trace/trace_probe.h | 7 --- + kernel/trace/trace_uprobe.c | 2 - + 6 files changed, 103 insertions(+), 103 deletions(-) + +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -7907,6 +7907,92 @@ void ftrace_dump(enum ftrace_dump_mode o + } + EXPORT_SYMBOL_GPL(ftrace_dump); + ++int trace_run_command(const char *buf, int (*createfn)(int, char **)) ++{ ++ char **argv; ++ int argc, ret; ++ ++ argc = 0; ++ ret = 0; ++ argv = argv_split(GFP_KERNEL, buf, &argc); ++ if (!argv) ++ return -ENOMEM; ++ ++ if (argc) ++ ret = createfn(argc, argv); ++ ++ argv_free(argv); ++ ++ return ret; ++} ++ ++#define WRITE_BUFSIZE 4096 ++ ++ssize_t trace_parse_run_command(struct file *file, const char __user *buffer, ++ size_t count, loff_t *ppos, ++ int (*createfn)(int, char **)) ++{ ++ char *kbuf, *buf, *tmp; ++ int ret = 0; ++ size_t done = 0; ++ size_t size; ++ ++ kbuf = kmalloc(WRITE_BUFSIZE, GFP_KERNEL); ++ if (!kbuf) ++ return -ENOMEM; ++ ++ while (done < count) { ++ size = count - done; ++ ++ if (size >= WRITE_BUFSIZE) ++ size = WRITE_BUFSIZE - 1; ++ ++ if (copy_from_user(kbuf, buffer + done, size)) { ++ ret = -EFAULT; ++ goto out; ++ } ++ kbuf[size] = '\0'; ++ buf = kbuf; ++ do { ++ tmp = strchr(buf, '\n'); ++ if (tmp) { ++ *tmp = '\0'; ++ size = tmp - buf + 1; ++ } else { ++ size = strlen(buf); ++ if (done + size < count) { ++ if (buf != kbuf) ++ break; ++ /* This can accept WRITE_BUFSIZE - 2 ('\n' + '\0') */ ++ pr_warn("Line length is too long: Should be less than %d\n", ++ WRITE_BUFSIZE - 2); ++ ret = -EINVAL; ++ goto out; ++ } ++ } ++ done += size; ++ ++ /* Remove comments */ ++ tmp = strchr(buf, '#'); ++ ++ if (tmp) ++ *tmp = '\0'; ++ ++ ret = trace_run_command(buf, createfn); ++ if (ret) ++ goto out; ++ buf += size; ++ ++ } while (done < count); ++ } ++ ret = done; ++ ++out: ++ kfree(kbuf); ++ ++ return ret; ++} ++ + __init static int tracer_alloc_buffers(void) + { + int ring_buf_size; +--- a/kernel/trace/trace.h ++++ b/kernel/trace/trace.h +@@ -1650,6 +1650,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); + ++#define MAX_EVENT_NAME_LEN 64 ++ ++extern int trace_run_command(const char *buf, int (*createfn)(int, char**)); ++extern ssize_t trace_parse_run_command(struct file *file, ++ const char __user *buffer, size_t count, loff_t *ppos, ++ int (*createfn)(int, char**)); ++ + /* + * Normal trace_printk() and friends allocates special buffers + * to do the manipulation, as well as saves the print formats +--- a/kernel/trace/trace_kprobe.c ++++ b/kernel/trace/trace_kprobe.c +@@ -878,8 +878,8 @@ static int probes_open(struct inode *ino + static ssize_t probes_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) + { +- return traceprobe_probes_write(file, buffer, count, ppos, +- create_trace_kprobe); ++ return trace_parse_run_command(file, buffer, count, ppos, ++ create_trace_kprobe); + } + + static const struct file_operations kprobe_events_ops = { +@@ -1404,9 +1404,9 @@ static __init int kprobe_trace_self_test + + pr_info("Testing kprobe tracing: "); + +- ret = traceprobe_command("p:testprobe kprobe_trace_selftest_target " +- "$stack $stack0 +0($stack)", +- create_trace_kprobe); ++ ret = trace_run_command("p:testprobe kprobe_trace_selftest_target " ++ "$stack $stack0 +0($stack)", ++ create_trace_kprobe); + if (WARN_ON_ONCE(ret)) { + pr_warn("error on probing function entry.\n"); + warn++; +@@ -1426,8 +1426,8 @@ static __init int kprobe_trace_self_test + } + } + +- ret = traceprobe_command("r:testprobe2 kprobe_trace_selftest_target " +- "$retval", create_trace_kprobe); ++ ret = trace_run_command("r:testprobe2 kprobe_trace_selftest_target " ++ "$retval", create_trace_kprobe); + if (WARN_ON_ONCE(ret)) { + pr_warn("error on probing function return.\n"); + warn++; +@@ -1497,13 +1497,13 @@ static __init int kprobe_trace_self_test + disable_trace_kprobe(tk, file); + } + +- ret = traceprobe_command("-:testprobe", create_trace_kprobe); ++ ret = trace_run_command("-:testprobe", create_trace_kprobe); + if (WARN_ON_ONCE(ret)) { + pr_warn("error on deleting a probe.\n"); + warn++; + } + +- ret = traceprobe_command("-:testprobe2", create_trace_kprobe); ++ ret = trace_run_command("-:testprobe2", create_trace_kprobe); + if (WARN_ON_ONCE(ret)) { + pr_warn("error on deleting a probe.\n"); + warn++; +--- a/kernel/trace/trace_probe.c ++++ b/kernel/trace/trace_probe.c +@@ -623,92 +623,6 @@ void traceprobe_free_probe_arg(struct pr + kfree(arg->comm); + } + +-int traceprobe_command(const char *buf, int (*createfn)(int, char **)) +-{ +- char **argv; +- int argc, ret; +- +- argc = 0; +- ret = 0; +- argv = argv_split(GFP_KERNEL, buf, &argc); +- if (!argv) +- return -ENOMEM; +- +- if (argc) +- ret = createfn(argc, argv); +- +- argv_free(argv); +- +- return ret; +-} +- +-#define WRITE_BUFSIZE 4096 +- +-ssize_t traceprobe_probes_write(struct file *file, const char __user *buffer, +- size_t count, loff_t *ppos, +- int (*createfn)(int, char **)) +-{ +- char *kbuf, *buf, *tmp; +- int ret = 0; +- size_t done = 0; +- size_t size; +- +- kbuf = kmalloc(WRITE_BUFSIZE, GFP_KERNEL); +- if (!kbuf) +- return -ENOMEM; +- +- while (done < count) { +- size = count - done; +- +- if (size >= WRITE_BUFSIZE) +- size = WRITE_BUFSIZE - 1; +- +- if (copy_from_user(kbuf, buffer + done, size)) { +- ret = -EFAULT; +- goto out; +- } +- kbuf[size] = '\0'; +- buf = kbuf; +- do { +- tmp = strchr(buf, '\n'); +- if (tmp) { +- *tmp = '\0'; +- size = tmp - buf + 1; +- } else { +- size = strlen(buf); +- if (done + size < count) { +- if (buf != kbuf) +- break; +- /* This can accept WRITE_BUFSIZE - 2 ('\n' + '\0') */ +- pr_warn("Line length is too long: Should be less than %d\n", +- WRITE_BUFSIZE - 2); +- ret = -EINVAL; +- goto out; +- } +- } +- done += size; +- +- /* Remove comments */ +- tmp = strchr(buf, '#'); +- +- if (tmp) +- *tmp = '\0'; +- +- ret = traceprobe_command(buf, createfn); +- if (ret) +- goto out; +- buf += size; +- +- } while (done < count); +- } +- ret = done; +- +-out: +- kfree(kbuf); +- +- return ret; +-} +- + static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, + bool is_return) + { +--- a/kernel/trace/trace_probe.h ++++ b/kernel/trace/trace_probe.h +@@ -42,7 +42,6 @@ + + #define MAX_TRACE_ARGS 128 + #define MAX_ARGSTR_LEN 63 +-#define MAX_EVENT_NAME_LEN 64 + #define MAX_STRING_SIZE PATH_MAX + + /* Reserved field names */ +@@ -356,12 +355,6 @@ extern void traceprobe_free_probe_arg(st + + extern int traceprobe_split_symbol_offset(char *symbol, unsigned long *offset); + +-extern ssize_t traceprobe_probes_write(struct file *file, +- const char __user *buffer, size_t count, loff_t *ppos, +- int (*createfn)(int, char**)); +- +-extern int traceprobe_command(const char *buf, int (*createfn)(int, char**)); +- + /* Sum up total data length for dynamic arraies (strings) */ + static nokprobe_inline int + __get_data_size(struct trace_probe *tp, struct pt_regs *regs) +--- a/kernel/trace/trace_uprobe.c ++++ b/kernel/trace/trace_uprobe.c +@@ -651,7 +651,7 @@ static int probes_open(struct inode *ino + static ssize_t probes_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) + { +- return traceprobe_probes_write(file, buffer, count, ppos, create_trace_uprobe); ++ return trace_parse_run_command(file, buffer, count, ppos, create_trace_uprobe); + } + + static const struct file_operations uprobe_events_ops = { diff --git a/patches/0010-KVM-PPC-Book3S-HV-Use-cpuhp_setup_state_nocalls_cpus.patch b/patches/0010-KVM-PPC-Book3S-HV-Use-cpuhp_setup_state_nocalls_cpus.patch index 04af41d371ff4..42c661b8a0c9a 100644 --- a/patches/0010-KVM-PPC-Book3S-HV-Use-cpuhp_setup_state_nocalls_cpus.patch +++ b/patches/0010-KVM-PPC-Book3S-HV-Use-cpuhp_setup_state_nocalls_cpus.patch @@ -1,4 +1,3 @@ -From 419af25fa4d0974fd758a668c08c369c19392a47 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Wed, 24 May 2017 10:15:21 +0200 Subject: [PATCH 10/32] KVM/PPC/Book3S HV: Use diff --git a/patches/0010-cpufreq-sh-Replace-racy-task-affinity-logic.patch b/patches/0010-cpufreq-sh-Replace-racy-task-affinity-logic.patch index 55de7d033a2a6..2d78323acf17c 100644 --- a/patches/0010-cpufreq-sh-Replace-racy-task-affinity-logic.patch +++ b/patches/0010-cpufreq-sh-Replace-racy-task-affinity-logic.patch @@ -1,4 +1,3 @@ -From 205dcc1ecbc566cbc20acf246e68de3b080b3ecf Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 12 Apr 2017 22:07:36 +0200 Subject: [PATCH 10/13] cpufreq/sh: Replace racy task affinity logic diff --git a/patches/0010-iommu-vt-d-Adjust-system_state-checks.patch b/patches/0010-iommu-vt-d-Adjust-system_state-checks.patch index 8b5535e280e4f..fb61919578d31 100644 --- a/patches/0010-iommu-vt-d-Adjust-system_state-checks.patch +++ b/patches/0010-iommu-vt-d-Adjust-system_state-checks.patch @@ -1,4 +1,3 @@ -From b608fe356fe8328665445a26ec75dfac918c8c5d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:41 +0200 Subject: [PATCH 10/17] iommu/vt-d: Adjust system_state checks diff --git a/patches/0010-tracing-Add-NO_DISCARD-event-file-flag.patch b/patches/0010-tracing-Add-NO_DISCARD-event-file-flag.patch new file mode 100644 index 0000000000000..c738f5638d1d4 --- /dev/null +++ b/patches/0010-tracing-Add-NO_DISCARD-event-file-flag.patch @@ -0,0 +1,105 @@ +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/0011-cpufreq-sparc-us3-Replace-racy-task-affinity-logic.patch b/patches/0011-cpufreq-sparc-us3-Replace-racy-task-affinity-logic.patch index ede702b81c21b..5a64452a7dbe7 100644 --- a/patches/0011-cpufreq-sparc-us3-Replace-racy-task-affinity-logic.patch +++ b/patches/0011-cpufreq-sparc-us3-Replace-racy-task-affinity-logic.patch @@ -1,4 +1,3 @@ -From 9fe24c4e92d3963d92d7d383e28ed098bd5689d8 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 12 Apr 2017 22:07:37 +0200 Subject: [PATCH 11/13] cpufreq/sparc-us3: Replace racy task affinity logic diff --git a/patches/0011-hwtracing-coresight-etm3x-Use-cpuhp_setup_state_noca.patch b/patches/0011-hwtracing-coresight-etm3x-Use-cpuhp_setup_state_noca.patch index 9e3f51dfc4380..c1a78e06ed8a9 100644 --- a/patches/0011-hwtracing-coresight-etm3x-Use-cpuhp_setup_state_noca.patch +++ b/patches/0011-hwtracing-coresight-etm3x-Use-cpuhp_setup_state_noca.patch @@ -1,4 +1,3 @@ -From e560c89c8ac0baadf0da351f602c599016568fc7 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Wed, 24 May 2017 10:15:22 +0200 Subject: [PATCH 11/32] hwtracing/coresight-etm3x: Use 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 new file mode 100644 index 0000000000000..bb63da5961c24 --- /dev/null +++ b/patches/0011-tracing-Add-post-trigger-flag-to-hist-trigger-comman.patch @@ -0,0 +1,28 @@ +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/0012-async-Adjust-system_state-checks.patch b/patches/0012-async-Adjust-system_state-checks.patch index 7de0d1fc371f6..1df1d996665c2 100644 --- a/patches/0012-async-Adjust-system_state-checks.patch +++ b/patches/0012-async-Adjust-system_state-checks.patch @@ -1,4 +1,3 @@ -From b4def42724594cd399cfee365221f5b38639711d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:43 +0200 Subject: [PATCH 12/17] async: Adjust system_state checks diff --git a/patches/0012-cpufreq-sparc-us2e-Replace-racy-task-affinity-logic.patch b/patches/0012-cpufreq-sparc-us2e-Replace-racy-task-affinity-logic.patch index 4f8155a4ea8ac..abf8a75f58382 100644 --- a/patches/0012-cpufreq-sparc-us2e-Replace-racy-task-affinity-logic.patch +++ b/patches/0012-cpufreq-sparc-us2e-Replace-racy-task-affinity-logic.patch @@ -1,4 +1,3 @@ -From 12699ac53a2e5fbd1fd7c164b11685d55c8aa28b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Thu, 13 Apr 2017 10:22:43 +0200 Subject: [PATCH 12/13] cpufreq/sparc-us2e: Replace racy task affinity logic diff --git a/patches/0012-hwtracing-coresight-etm4x-Use-cpuhp_setup_state_noca.patch b/patches/0012-hwtracing-coresight-etm4x-Use-cpuhp_setup_state_noca.patch index f4055c9951118..eb1ca661c6573 100644 --- a/patches/0012-hwtracing-coresight-etm4x-Use-cpuhp_setup_state_noca.patch +++ b/patches/0012-hwtracing-coresight-etm4x-Use-cpuhp_setup_state_noca.patch @@ -1,4 +1,3 @@ -From e9f5d63f84febb7e9dfe4e0dc696adf88053fbf2 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Wed, 24 May 2017 10:15:23 +0200 Subject: [PATCH 12/32] hwtracing/coresight-etm4x: Use diff --git a/patches/0012-tracing-Add-hist-trigger-timestamp-support.patch b/patches/0012-tracing-Add-hist-trigger-timestamp-support.patch new file mode 100644 index 0000000000000..3092c4117e9d2 --- /dev/null +++ b/patches/0012-tracing-Add-hist-trigger-timestamp-support.patch @@ -0,0 +1,231 @@ +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 + +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 +is really part of the corresponding ring buffer event. + +To make use of the timestamp field, users can specify +"$common_timestamp" as a field name for any histogram. Note that this +doesn't make much sense on its own either as either a key or value, +but needs to be supported even so, since follow-on patches will add +support for making use of this field in time deltas. The '$' is used +as a prefix on the variable name to indicate that it's not an bonafide +event field - so you won't find it in the event description - but +rather it's a synthetic field that can be used like a real field). + +Note that the use of this field requires the ring buffer be put into +TIME_EXTEND_ABS mode, which saves the complete timestamp for each +event rather than an offset. This mode will be enabled if and only if +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(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -89,6 +89,12 @@ static u64 hist_field_log2(struct hist_f + return (u64) ilog2(roundup_pow_of_two(val)); + } + ++static u64 hist_field_timestamp(struct hist_field *hist_field, void *event, ++ struct ring_buffer_event *rbe) ++{ ++ return ring_buffer_event_time_stamp(rbe); ++} ++ + #define DEFINE_HIST_FIELD_FN(type) \ + static u64 hist_field_##type(struct hist_field *hist_field, \ + void *event, \ +@@ -135,6 +141,7 @@ enum hist_field_flags { + HIST_FIELD_FL_SYSCALL = 128, + HIST_FIELD_FL_STACKTRACE = 256, + HIST_FIELD_FL_LOG2 = 512, ++ HIST_FIELD_FL_TIMESTAMP = 1024, + }; + + struct hist_trigger_attrs { +@@ -159,6 +166,7 @@ struct hist_trigger_data { + struct trace_event_file *event_file; + struct hist_trigger_attrs *attrs; + struct tracing_map *map; ++ bool enable_timestamps; + }; + + static const char *hist_field_name(struct hist_field *field, +@@ -173,6 +181,8 @@ static const char *hist_field_name(struc + field_name = field->field->name; + else if (field->flags & HIST_FIELD_FL_LOG2) + field_name = hist_field_name(field->operands[0], ++level); ++ else if (field->flags & HIST_FIELD_FL_TIMESTAMP) ++ field_name = "$common_timestamp"; + + if (field_name == NULL) + field_name = ""; +@@ -435,6 +445,12 @@ static struct hist_field *create_hist_fi + goto out; + } + ++ if (flags & HIST_FIELD_FL_TIMESTAMP) { ++ hist_field->fn = hist_field_timestamp; ++ hist_field->size = sizeof(u64); ++ goto out; ++ } ++ + if (WARN_ON_ONCE(!field)) + goto out; + +@@ -512,10 +528,15 @@ static int create_val_field(struct hist_ + } + } + +- field = trace_find_event_field(file->event_call, field_name); +- if (!field) { +- ret = -EINVAL; +- 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) { ++ 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_ + } + } + +- field = trace_find_event_field(file->event_call, field_name); +- if (!field) { +- ret = -EINVAL; +- goto out; +- } ++ if (strcmp(field_name, "$common_timestamp") == 0) { ++ flags |= HIST_FIELD_FL_TIMESTAMP; ++ hist_data->enable_timestamps = true; ++ key_size = sizeof(u64); ++ } else { ++ field = trace_find_event_field(file->event_call, field_name); ++ if (!field) { ++ ret = -EINVAL; ++ goto out; ++ } + +- if (is_string_field(field)) +- key_size = MAX_FILTER_STR_VAL; +- else +- key_size = field->size; ++ if (is_string_field(field)) ++ key_size = MAX_FILTER_STR_VAL; ++ else ++ key_size = field->size; ++ } + } + + 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 + + if (hist_field->flags & HIST_FIELD_FL_STACKTRACE) + cmp_fn = tracing_map_cmp_none; ++ else if (!field) ++ cmp_fn = tracing_map_cmp_num(hist_field->size, ++ hist_field->is_signed); + else if (is_string_field(field)) + cmp_fn = tracing_map_cmp_string; + else +@@ -1213,7 +1243,11 @@ static void hist_field_print(struct seq_ + { + const char *field_name = hist_field_name(hist_field, 0); + +- seq_printf(m, "%s", field_name); ++ if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP) ++ seq_puts(m, "$common_timestamp"); ++ else if (field_name) ++ seq_printf(m, "%s", field_name); ++ + 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 + + for (i = 0; i < hist_data->n_sort_keys; i++) { + struct tracing_map_sort_key *sort_key; ++ unsigned int idx; + + sort_key = &hist_data->sort_keys[i]; ++ idx = sort_key->field_idx; ++ ++ if (WARN_ON(idx >= TRACING_MAP_FIELDS_MAX)) ++ return -EINVAL; + + if (i > 0) + seq_puts(m, ","); + +- if (sort_key->field_idx == HITCOUNT_IDX) ++ if (idx == HITCOUNT_IDX) + seq_puts(m, "hitcount"); +- else { +- unsigned int idx = sort_key->field_idx; +- +- if (WARN_ON(idx >= TRACING_MAP_FIELDS_MAX)) +- return -EINVAL; +- ++ else + hist_field_print(m, hist_data->fields[idx]); +- } + + if (sort_key->descending) + seq_puts(m, ".descending"); + } +- + 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 + return false; + if (key_field->offset != key_field_test->offset) + return false; ++ if (key_field->size != key_field_test->size) ++ return false; ++ if (key_field->is_signed != key_field_test->is_signed) ++ return false; + } + + for (i = 0; i < hist_data->n_sort_keys; i++) { +@@ -1534,6 +1570,9 @@ static int hist_register_trigger(char *g + + update_cond_flag(file); + ++ if (hist_data->enable_timestamps) ++ tracing_set_time_stamp_abs(file->tr, true); ++ + 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 + + if (unregistered && test->ops->free) + test->ops->free(test->ops, test); ++ ++ if (hist_data->enable_timestamps) ++ tracing_set_time_stamp_abs(file->tr, false); + } + + static void hist_unreg_all(struct trace_event_file *file) diff --git a/patches/0013-crypto-N2-Replace-racy-task-affinity-logic.patch b/patches/0013-crypto-N2-Replace-racy-task-affinity-logic.patch index 4cbb7f554e299..d76c717cf6503 100644 --- a/patches/0013-crypto-N2-Replace-racy-task-affinity-logic.patch +++ b/patches/0013-crypto-N2-Replace-racy-task-affinity-logic.patch @@ -1,4 +1,3 @@ -From 73810a069120aa831debb4d967310ab900f628ad Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Thu, 13 Apr 2017 10:20:23 +0200 Subject: [PATCH 13/13] crypto: N2 - Replace racy task affinity logic diff --git a/patches/0013-extable-Adjust-system_state-checks.patch b/patches/0013-extable-Adjust-system_state-checks.patch index b11d7de46e051..c3c3175841cb2 100644 --- a/patches/0013-extable-Adjust-system_state-checks.patch +++ b/patches/0013-extable-Adjust-system_state-checks.patch @@ -1,4 +1,3 @@ -From 0594729c24d846889408a07057b5cc9e8d931419 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:44 +0200 Subject: [PATCH 13/17] extable: Adjust system_state checks diff --git a/patches/0013-perf-x86-intel-cqm-Use-cpuhp_setup_state_cpuslocked.patch b/patches/0013-perf-x86-intel-cqm-Use-cpuhp_setup_state_cpuslocked.patch index 883a08ddb1714..0df26fdcedd6a 100644 --- a/patches/0013-perf-x86-intel-cqm-Use-cpuhp_setup_state_cpuslocked.patch +++ b/patches/0013-perf-x86-intel-cqm-Use-cpuhp_setup_state_cpuslocked.patch @@ -1,4 +1,3 @@ -From 04b247c2ebdd6ba1c46c7c22546229a89760b43a Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Wed, 24 May 2017 10:15:24 +0200 Subject: [PATCH 13/32] perf/x86/intel/cqm: Use cpuhp_setup_state_cpuslocked() diff --git a/patches/0013-tracing-Add-per-element-variable-support-to-tracing_.patch b/patches/0013-tracing-Add-per-element-variable-support-to-tracing_.patch new file mode 100644 index 0000000000000..4d05c895072d7 --- /dev/null +++ b/patches/0013-tracing-Add-per-element-variable-support-to-tracing_.patch @@ -0,0 +1,232 @@ +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 + tracing_map + +In order to allow information to be passed between trace events, add +support for per-element variables to tracing_map. This provides a +means for histograms to associate a value or values with an entry when +it's saved or updated, and retrieved by a subsequent event occurrences. + +Variables can be set using tracing_map_set_var() and read using +tracing_map_read_var(). tracing_map_var_set() returns true or false +depending on whether or not the variable has been set or not, which is +important for event-matching applications. + +tracing_map_read_var_once() reads the variable and resets it to the +'unset' state, implementing read-once variables, which are also +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.h | 11 ++++ + 2 files changed, 124 insertions(+) + +--- a/kernel/trace/tracing_map.c ++++ b/kernel/trace/tracing_map.c +@@ -66,6 +66,73 @@ u64 tracing_map_read_sum(struct tracing_ + return (u64)atomic64_read(&elt->fields[i].sum); + } + ++/** ++ * tracing_map_set_var - Assign a tracing_map_elt's variable field ++ * @elt: The tracing_map_elt ++ * @i: The index of the given variable associated with the tracing_map_elt ++ * @n: The value to assign ++ * ++ * Assign n to variable i associated with the specified tracing_map_elt ++ * instance. The index i is the index returned by the call to ++ * tracing_map_add_var() when the tracing map was set up. ++ */ ++void tracing_map_set_var(struct tracing_map_elt *elt, unsigned int i, u64 n) ++{ ++ atomic64_set(&elt->vars[i], n); ++ elt->var_set[i] = true; ++} ++ ++/** ++ * tracing_map_var_set - Return whether or not a variable has been set ++ * @elt: The tracing_map_elt ++ * @i: The index of the given variable associated with the tracing_map_elt ++ * ++ * Return true if the variable has been set, false otherwise. The ++ * index i is the index returned by the call to tracing_map_add_var() ++ * when the tracing map was set up. ++ */ ++bool tracing_map_var_set(struct tracing_map_elt *elt, unsigned int i) ++{ ++ return elt->var_set[i]; ++} ++ ++/** ++ * tracing_map_read_var - Return the value of a tracing_map_elt's variable field ++ * @elt: The tracing_map_elt ++ * @i: The index of the given variable associated with the tracing_map_elt ++ * ++ * Retrieve the value of the variable i associated with the specified ++ * tracing_map_elt instance. The index i is the index returned by the ++ * call to tracing_map_add_var() when the tracing map was set ++ * up. ++ * ++ * Return: The variable value associated with field i for elt. ++ */ ++u64 tracing_map_read_var(struct tracing_map_elt *elt, unsigned int i) ++{ ++ return (u64)atomic64_read(&elt->vars[i]); ++} ++ ++/** ++ * tracing_map_read_var_once - Return and reset a tracing_map_elt's variable field ++ * @elt: The tracing_map_elt ++ * @i: The index of the given variable associated with the tracing_map_elt ++ * ++ * Retrieve the value of the variable i associated with the specified ++ * tracing_map_elt instance, and reset the variable to the 'not set' ++ * state. The index i is the index returned by the call to ++ * tracing_map_add_var() when the tracing map was set up. The reset ++ * essentially makes the variable a read-once variable if it's only ++ * accessed using this function. ++ * ++ * Return: The variable value associated with field i for elt. ++ */ ++u64 tracing_map_read_var_once(struct tracing_map_elt *elt, unsigned int i) ++{ ++ elt->var_set[i] = false; ++ return (u64)atomic64_read(&elt->vars[i]); ++} ++ + int tracing_map_cmp_string(void *val_a, void *val_b) + { + char *a = val_a; +@@ -171,6 +238,28 @@ int tracing_map_add_sum_field(struct tra + } + + /** ++ * tracing_map_add_var - Add a field describing a tracing_map var ++ * @map: The tracing_map ++ * ++ * Add a var to the map and return the index identifying it in the map ++ * and associated tracing_map_elts. This is the index used for ++ * instance to update a var for a particular tracing_map_elt using ++ * tracing_map_update_var() or reading it via tracing_map_read_var(). ++ * ++ * Return: The index identifying the var in the map and associated ++ * tracing_map_elts, or -EINVAL on error. ++ */ ++int tracing_map_add_var(struct tracing_map *map) ++{ ++ int ret = -EINVAL; ++ ++ if (map->n_vars < TRACING_MAP_VARS_MAX) ++ ret = map->n_vars++; ++ ++ return ret; ++} ++ ++/** + * tracing_map_add_key_field - Add a field describing a tracing_map key + * @map: The tracing_map + * @offset: The offset within the key +@@ -277,6 +366,11 @@ static void tracing_map_elt_clear(struct + if (elt->fields[i].cmp_fn == tracing_map_cmp_atomic64) + atomic64_set(&elt->fields[i].sum, 0); + ++ for (i = 0; i < elt->map->n_vars; i++) { ++ atomic64_set(&elt->vars[i], 0); ++ elt->var_set[i] = false; ++ } ++ + if (elt->map->ops && elt->map->ops->elt_clear) + elt->map->ops->elt_clear(elt); + } +@@ -303,6 +397,8 @@ static void tracing_map_elt_free(struct + if (elt->map->ops && elt->map->ops->elt_free) + elt->map->ops->elt_free(elt); + kfree(elt->fields); ++ kfree(elt->vars); ++ kfree(elt->var_set); + kfree(elt->key); + kfree(elt); + } +@@ -330,6 +426,18 @@ static struct tracing_map_elt *tracing_m + goto free; + } + ++ elt->vars = kcalloc(map->n_vars, sizeof(*elt->vars), GFP_KERNEL); ++ if (!elt->vars) { ++ err = -ENOMEM; ++ goto free; ++ } ++ ++ elt->var_set = kcalloc(map->n_vars, sizeof(*elt->var_set), GFP_KERNEL); ++ if (!elt->var_set) { ++ 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; + } + +--- a/kernel/trace/tracing_map.h ++++ b/kernel/trace/tracing_map.h +@@ -9,6 +9,7 @@ + #define TRACING_MAP_VALS_MAX 3 + #define TRACING_MAP_FIELDS_MAX (TRACING_MAP_KEYS_MAX + \ + TRACING_MAP_VALS_MAX) ++#define TRACING_MAP_VARS_MAX 16 + #define TRACING_MAP_SORT_KEYS_MAX 2 + + typedef int (*tracing_map_cmp_fn_t) (void *val_a, void *val_b); +@@ -136,6 +137,8 @@ struct tracing_map_field { + struct tracing_map_elt { + struct tracing_map *map; + struct tracing_map_field *fields; ++ atomic64_t *vars; ++ bool *var_set; + void *key; + void *private_data; + }; +@@ -191,6 +194,7 @@ struct tracing_map { + int key_idx[TRACING_MAP_KEYS_MAX]; + unsigned int n_keys; + struct tracing_map_sort_key sort_key; ++ unsigned int n_vars; + atomic64_t hits; + atomic64_t drops; + }; +@@ -247,6 +251,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); ++extern int tracing_map_add_var(struct tracing_map *map); + 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 + + extern void tracing_map_update_sum(struct tracing_map_elt *elt, + unsigned int i, u64 n); ++extern void tracing_map_set_var(struct tracing_map_elt *elt, ++ unsigned int i, u64 n); ++extern bool tracing_map_var_set(struct tracing_map_elt *elt, unsigned int i); + extern u64 tracing_map_read_sum(struct tracing_map_elt *elt, unsigned int i); ++extern u64 tracing_map_read_var(struct tracing_map_elt *elt, unsigned int i); ++extern u64 tracing_map_read_var_once(struct tracing_map_elt *elt, unsigned int i); ++ + extern void tracing_map_set_field_descr(struct tracing_map *map, + unsigned int i, + unsigned int key_offset, diff --git a/patches/0014-ARM-hw_breakpoint-Use-cpuhp_setup_state_cpuslocked.patch b/patches/0014-ARM-hw_breakpoint-Use-cpuhp_setup_state_cpuslocked.patch index 2f0309d5b9765..4ead97c3209be 100644 --- a/patches/0014-ARM-hw_breakpoint-Use-cpuhp_setup_state_cpuslocked.patch +++ b/patches/0014-ARM-hw_breakpoint-Use-cpuhp_setup_state_cpuslocked.patch @@ -1,4 +1,3 @@ -From fe2a5cd8aa038e2b02fda983afc2083e94c04b4f Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Wed, 24 May 2017 10:15:25 +0200 Subject: [PATCH 14/32] ARM/hw_breakpoint: Use cpuhp_setup_state_cpuslocked() diff --git a/patches/0014-printk-Adjust-system_state-checks.patch b/patches/0014-printk-Adjust-system_state-checks.patch index c1d9ee4d1166c..c729a84a04cc7 100644 --- a/patches/0014-printk-Adjust-system_state-checks.patch +++ b/patches/0014-printk-Adjust-system_state-checks.patch @@ -1,4 +1,3 @@ -From ff48cd26fc4889b9deb5f9333d3c61746e450b7f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:45 +0200 Subject: [PATCH 14/17] printk: Adjust system_state checks diff --git a/patches/0014-tracing-Add-hist_data-member-to-hist_field.patch b/patches/0014-tracing-Add-hist_data-member-to-hist_field.patch new file mode 100644 index 0000000000000..ae3d689550b34 --- /dev/null +++ b/patches/0014-tracing-Add-hist_data-member-to-hist_field.patch @@ -0,0 +1,78 @@ +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 + +Allow hist_data access via hist_field. Some users of hist_fields +require or will require more access to the associated hist_data. + +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 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -39,6 +39,7 @@ struct hist_field { + unsigned int offset; + unsigned int is_signed; + struct hist_field *operands[HIST_FIELD_OPERANDS_MAX]; ++ struct hist_trigger_data *hist_data; + }; + + static u64 hist_field_none(struct hist_field *field, void *event, +@@ -415,7 +416,8 @@ static void destroy_hist_field(struct hi + kfree(hist_field); + } + +-static struct hist_field *create_hist_field(struct ftrace_event_field *field, ++static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data, ++ struct ftrace_event_field *field, + unsigned long flags) + { + struct hist_field *hist_field; +@@ -427,6 +429,8 @@ static struct hist_field *create_hist_fi + if (!hist_field) + return NULL; + ++ hist_field->hist_data = hist_data; ++ + 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 + if (flags & HIST_FIELD_FL_LOG2) { + unsigned long fl = flags & ~HIST_FIELD_FL_LOG2; + hist_field->fn = hist_field_log2; +- hist_field->operands[0] = create_hist_field(field, fl); ++ hist_field->operands[0] = create_hist_field(hist_data, field, fl); + hist_field->size = hist_field->operands[0]->size; + goto out; + } +@@ -493,7 +497,7 @@ static void destroy_hist_fields(struct h + static int create_hitcount_val(struct hist_trigger_data *hist_data) + { + hist_data->fields[HITCOUNT_IDX] = +- create_hist_field(NULL, HIST_FIELD_FL_HITCOUNT); ++ create_hist_field(hist_data, NULL, HIST_FIELD_FL_HITCOUNT); + if (!hist_data->fields[HITCOUNT_IDX]) + return -ENOMEM; + +@@ -539,7 +543,7 @@ static int create_val_field(struct hist_ + } + } + +- hist_data->fields[val_idx] = create_hist_field(field, flags); ++ hist_data->fields[val_idx] = create_hist_field(hist_data, field, flags); + if (!hist_data->fields[val_idx]) { + ret = -ENOMEM; + goto out; +@@ -649,7 +653,7 @@ static int create_key_field(struct hist_ + } + } + +- hist_data->fields[key_idx] = create_hist_field(field, flags); ++ hist_data->fields[key_idx] = create_hist_field(hist_data, field, flags); + if (!hist_data->fields[key_idx]) { + ret = -ENOMEM; + goto out; diff --git a/patches/0015-mm-vmscan-Adjust-system_state-checks.patch b/patches/0015-mm-vmscan-Adjust-system_state-checks.patch index 9fef94c667a9b..8c06086f2d2da 100644 --- a/patches/0015-mm-vmscan-Adjust-system_state-checks.patch +++ b/patches/0015-mm-vmscan-Adjust-system_state-checks.patch @@ -1,4 +1,3 @@ -From c6202adf3a0969514299cf10ff07376a84ad09bb Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:46 +0200 Subject: [PATCH 15/17] mm/vmscan: Adjust system_state checks diff --git a/patches/0015-s390-kernel-Use-stop_machine_cpuslocked.patch b/patches/0015-s390-kernel-Use-stop_machine_cpuslocked.patch index da46dc48abd68..8a4902d43ceeb 100644 --- a/patches/0015-s390-kernel-Use-stop_machine_cpuslocked.patch +++ b/patches/0015-s390-kernel-Use-stop_machine_cpuslocked.patch @@ -1,4 +1,3 @@ -From 2337e879e8805a630b418f3e73a98084d4724b83 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Wed, 24 May 2017 10:15:26 +0200 Subject: [PATCH 15/32] s390/kernel: Use stop_machine_cpuslocked() diff --git a/patches/0015-tracing-Add-usecs-modifier-for-hist-trigger-timestam.patch b/patches/0015-tracing-Add-usecs-modifier-for-hist-trigger-timestam.patch new file mode 100644 index 0000000000000..b90640f231828 --- /dev/null +++ b/patches/0015-tracing-Add-usecs-modifier-for-hist-trigger-timestam.patch @@ -0,0 +1,130 @@ +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 + +Appending .usecs onto a common_timestamp field will cause the +timestamp value to be in microseconds instead of the default +nanoseconds. A typical latency histogram using usecs would look like +this: + + # echo 'hist:keys=pid,prio:ts0=$common_timestamp.usecs ... + # echo 'hist:keys=next_pid:wakeup_lat=$common_timestamp.usecs-$ts0 ... + +This also adds an external trace_clock_in_ns() to trace.c for the +timestamp conversion. + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/trace.c | 8 ++++++++ + kernel/trace/trace.h | 2 ++ + kernel/trace/trace_events_hist.c | 28 ++++++++++++++++++++++------ + 3 files changed, 32 insertions(+), 6 deletions(-) + +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -1164,6 +1164,14 @@ static struct { + ARCH_TRACE_CLOCKS + }; + ++bool trace_clock_in_ns(struct trace_array *tr) ++{ ++ if (trace_clocks[tr->clock_id].in_ns) ++ return true; ++ ++ return false; ++} ++ + /* + * trace_parser_get_init - gets the buffer for trace parser + */ +--- a/kernel/trace/trace.h ++++ b/kernel/trace/trace.h +@@ -280,6 +280,8 @@ extern void trace_array_put(struct trace + + extern int tracing_set_time_stamp_abs(struct trace_array *tr, bool abs); + ++extern bool trace_clock_in_ns(struct trace_array *tr); ++ + /* + * The global tracer (top) should be the first trace array added, + * but we check the flag anyway. +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -90,12 +90,6 @@ static u64 hist_field_log2(struct hist_f + return (u64) ilog2(roundup_pow_of_two(val)); + } + +-static u64 hist_field_timestamp(struct hist_field *hist_field, void *event, +- struct ring_buffer_event *rbe) +-{ +- return ring_buffer_event_time_stamp(rbe); +-} +- + #define DEFINE_HIST_FIELD_FN(type) \ + static u64 hist_field_##type(struct hist_field *hist_field, \ + void *event, \ +@@ -143,6 +137,7 @@ enum hist_field_flags { + HIST_FIELD_FL_STACKTRACE = 256, + HIST_FIELD_FL_LOG2 = 512, + HIST_FIELD_FL_TIMESTAMP = 1024, ++ HIST_FIELD_FL_TIMESTAMP_USECS = 2048, + }; + + struct hist_trigger_attrs { +@@ -153,6 +148,7 @@ struct hist_trigger_attrs { + bool pause; + bool cont; + bool clear; ++ bool ts_in_usecs; + unsigned int map_bits; + }; + +@@ -170,6 +166,20 @@ struct hist_trigger_data { + bool enable_timestamps; + }; + ++static u64 hist_field_timestamp(struct hist_field *hist_field, void *event, ++ struct ring_buffer_event *rbe) ++{ ++ struct hist_trigger_data *hist_data = hist_field->hist_data; ++ struct trace_array *tr = hist_data->event_file->tr; ++ ++ u64 ts = ring_buffer_event_time_stamp(rbe); ++ ++ if (hist_data->attrs->ts_in_usecs && trace_clock_in_ns(tr)) ++ ts = ns2usecs(ts); ++ ++ return ts; ++} ++ + static const char *hist_field_name(struct hist_field *field, + unsigned int level) + { +@@ -629,6 +639,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; ++ else if (strcmp(field_str, "usecs") == 0) ++ flags |= HIST_FIELD_FL_TIMESTAMP_USECS; + else { + ret = -EINVAL; + goto out; +@@ -638,6 +650,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; ++ if (flags & HIST_FIELD_FL_TIMESTAMP_USECS) ++ hist_data->attrs->ts_in_usecs = true; + 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( + 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; + } diff --git a/patches/0016-init-Introduce-SYSTEM_SCHEDULING-state.patch b/patches/0016-init-Introduce-SYSTEM_SCHEDULING-state.patch index 74763cdae860f..bbf5c6fc82b7a 100644 --- a/patches/0016-init-Introduce-SYSTEM_SCHEDULING-state.patch +++ b/patches/0016-init-Introduce-SYSTEM_SCHEDULING-state.patch @@ -1,4 +1,3 @@ -From 69a78ff226fe0241ab6cb9dd961667be477e3cf7 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:47 +0200 Subject: [PATCH 16/17] init: Introduce SYSTEM_SCHEDULING state diff --git a/patches/0016-powerpc-powernv-Use-stop_machine_cpuslocked.patch b/patches/0016-powerpc-powernv-Use-stop_machine_cpuslocked.patch index 0317958715c4e..c6e317d6a65b6 100644 --- a/patches/0016-powerpc-powernv-Use-stop_machine_cpuslocked.patch +++ b/patches/0016-powerpc-powernv-Use-stop_machine_cpuslocked.patch @@ -1,4 +1,3 @@ -From f9a69931c3959940538884d5962b770c3db75df5 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Wed, 24 May 2017 10:15:27 +0200 Subject: [PATCH 16/32] powerpc/powernv: Use stop_machine_cpuslocked() diff --git a/patches/0016-tracing-Add-variable-support-to-hist-triggers.patch b/patches/0016-tracing-Add-variable-support-to-hist-triggers.patch new file mode 100644 index 0000000000000..9d046d4a1dfe5 --- /dev/null +++ b/patches/0016-tracing-Add-variable-support-to-hist-triggers.patch @@ -0,0 +1,691 @@ +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 + +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. + +The basic syntax for saving a variable is to simply prefix a unique +variable name not corresponding to any keyword along with an '=' sign +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 ...' + +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 +as a value: + + # echo 'hist:keys=next_pid:ts1=common_timestamp:... + +Multiple variables can be assigned at the same time: + + # echo 'hist:keys=pid:vals=ts0=common_timestamp,b=field1,field2 ... + +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 ... + +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: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/trace_events_hist.c | 299 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 264 insertions(+), 35 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -30,6 +30,13 @@ typedef u64 (*hist_field_fn_t) (struct h + struct ring_buffer_event *rbe); + + #define HIST_FIELD_OPERANDS_MAX 2 ++#define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX) ++ ++struct hist_var { ++ char *name; ++ struct hist_trigger_data *hist_data; ++ unsigned int idx; ++}; + + struct hist_field { + struct ftrace_event_field *field; +@@ -40,6 +47,7 @@ struct hist_field { + unsigned int is_signed; + struct hist_field *operands[HIST_FIELD_OPERANDS_MAX]; + struct hist_trigger_data *hist_data; ++ struct hist_var var; + }; + + static u64 hist_field_none(struct hist_field *field, void *event, +@@ -138,6 +146,8 @@ 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 hist_trigger_attrs { +@@ -150,13 +160,18 @@ 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 hist_trigger_data { +- struct hist_field *fields[TRACING_MAP_FIELDS_MAX]; ++ struct hist_field *fields[HIST_FIELDS_MAX]; + unsigned int n_vals; + unsigned int n_keys; + unsigned int n_fields; ++ unsigned int n_vars; ++ unsigned int n_var_only; + 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 { + struct hist_trigger_attrs *attrs; + struct tracing_map *map; + bool enable_timestamps; ++ bool remove; + }; + + static u64 hist_field_timestamp(struct hist_field *hist_field, void *event, +@@ -262,9 +278,14 @@ static int parse_map_size(char *str) + + static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs) + { ++ unsigned int i; ++ + if (!attrs) + return; + ++ for (i = 0; i < attrs->n_assignments; i++) ++ kfree(attrs->assignment_str[i]); ++ + kfree(attrs->name); + kfree(attrs->sort_key_str); + kfree(attrs->keys_str); +@@ -295,8 +316,22 @@ static int parse_assignment(char *str, s + goto out; + } + attrs->map_bits = map_bits; +- } else +- ret = -EINVAL; ++ } else { ++ char *assignment; ++ ++ if (attrs->n_assignments == TRACING_MAP_VARS_MAX) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ assignment = kstrdup(str, GFP_KERNEL); ++ if (!assignment) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ attrs->assignment_str[attrs->n_assignments++] = assignment; ++ } + out: + return ret; + } +@@ -423,12 +458,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); + ++ kfree(hist_field->var.name); ++ + kfree(hist_field); + } + + static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data, + struct ftrace_event_field *field, +- unsigned long flags) ++ unsigned long flags, ++ char *var_name) + { + struct hist_field *hist_field; + +@@ -454,7 +492,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; +- hist_field->operands[0] = create_hist_field(hist_data, field, fl); ++ hist_field->operands[0] = create_hist_field(hist_data, field, fl, NULL); + hist_field->size = hist_field->operands[0]->size; + goto out; + } +@@ -489,14 +527,23 @@ static struct hist_field *create_hist_fi + hist_field->field = field; + hist_field->flags = flags; + ++ if (var_name) { ++ hist_field->var.name = kstrdup(var_name, GFP_KERNEL); ++ if (!hist_field->var.name) ++ goto free; ++ } ++ + return hist_field; ++ free: ++ destroy_hist_field(hist_field, 0); ++ return NULL; + } + + static void destroy_hist_fields(struct hist_trigger_data *hist_data) + { + unsigned int i; + +- for (i = 0; i < TRACING_MAP_FIELDS_MAX; i++) { ++ for (i = 0; i < HIST_FIELDS_MAX; i++) { + 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 + static int create_hitcount_val(struct hist_trigger_data *hist_data) + { + hist_data->fields[HITCOUNT_IDX] = +- create_hist_field(hist_data, NULL, HIST_FIELD_FL_HITCOUNT); ++ create_hist_field(hist_data, NULL, HIST_FIELD_FL_HITCOUNT, NULL); + if (!hist_data->fields[HITCOUNT_IDX]) + return -ENOMEM; + + hist_data->n_vals++; ++ hist_data->n_fields++; + + if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX)) + return -EINVAL; +@@ -519,19 +567,81 @@ static int create_hitcount_val(struct hi + return 0; + } + ++static struct hist_field *find_var_field(struct hist_trigger_data *hist_data, ++ const char *var_name) ++{ ++ struct hist_field *hist_field, *found = NULL; ++ int i; ++ ++ for_each_hist_field(i, hist_data) { ++ hist_field = hist_data->fields[i]; ++ if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR && ++ strcmp(hist_field->var.name, var_name) == 0) { ++ found = hist_field; ++ break; ++ } ++ } ++ ++ return found; ++} ++ ++static struct hist_field *find_var(struct trace_event_file *file, ++ const char *var_name) ++{ ++ struct hist_trigger_data *hist_data; ++ struct event_trigger_data *test; ++ struct hist_field *hist_field; ++ ++ list_for_each_entry_rcu(test, &file->triggers, list) { ++ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { ++ hist_data = test->private_data; ++ hist_field = find_var_field(hist_data, var_name); ++ if (hist_field) ++ return hist_field; ++ } ++ } ++ ++ 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) + { + struct ftrace_event_field *field = NULL; ++ char *field_name, *var_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; ++ } ++ + 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_ + } + } + +- hist_data->fields[val_idx] = create_hist_field(hist_data, field, flags); ++ 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->n_vals; ++ ++hist_data->n_fields; + +- if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX)) ++ if (hist_data->fields[val_idx]->flags & HIST_FIELD_FL_VAR_ONLY) ++ hist_data->n_var_only++; ++ ++ if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX + TRACING_MAP_VARS_MAX)) + ret = -EINVAL; + out: + return ret; +@@ -571,7 +685,7 @@ static int create_val_fields(struct hist + struct trace_event_file *file) + { + char *fields_str, *field_str; +- unsigned int i, j; ++ unsigned int i, j = 1; + int ret; + + ret = create_hitcount_val(hist_data); +@@ -591,12 +705,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); + if (ret) + goto out; + } ++ + if (fields_str && (strcmp(fields_str, "hitcount") != 0)) + ret = -EINVAL; + out: +@@ -610,18 +727,32 @@ 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)) ++ if (WARN_ON(key_idx >= HIST_FIELDS_MAX)) + 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; ++ } ++ + 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); + } else { + char *field_name = strsep(&field_str, "."); + +@@ -667,7 +798,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); + if (!hist_data->fields[key_idx]) { + ret = -ENOMEM; + goto out; +@@ -683,6 +814,7 @@ static int create_key_field(struct hist_ + } + + hist_data->n_keys++; ++ hist_data->n_fields++; + + if (WARN_ON(hist_data->n_keys > TRACING_MAP_KEYS_MAX)) + return -EINVAL; +@@ -726,6 +858,29 @@ 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; ++ 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; ++ } ++ } ++ out: ++ 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 + if (ret) + goto out; + +- ret = create_key_fields(hist_data, file); ++ ret = create_var_fields(hist_data, file); + if (ret) + goto out; + +- hist_data->n_fields = hist_data->n_vals + hist_data->n_keys; ++ ret = create_key_fields(hist_data, file); ++ if (ret) ++ goto out; + out: + return ret; + } +@@ -763,7 +920,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; +- unsigned int i, j; ++ unsigned int i, j, k; + + hist_data->n_sort_keys = 1; /* we always have at least one, hitcount */ + +@@ -811,13 +968,21 @@ 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; ++ + hist_field = hist_data->fields[j]; ++ if (hist_field->flags & HIST_FIELD_FL_VAR_ONLY) ++ continue; ++ ++ 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_ + break; + } + } ++ + hist_data->n_sort_keys = i; + out: + return ret; +@@ -872,12 +1038,19 @@ static int create_tracing_map_fields(str + idx = tracing_map_add_key_field(map, + hist_field->offset, + cmp_fn); +- +- } else ++ } else if (!(hist_field->flags & HIST_FIELD_FL_VAR)) + idx = tracing_map_add_sum_field(map); + + if (idx < 0) + return idx; ++ ++ if (hist_field->flags & HIST_FIELD_FL_VAR) { ++ idx = tracing_map_add_var(map); ++ if (idx < 0) ++ return idx; ++ hist_field->var.idx = idx; ++ hist_field->var.hist_data = hist_data; ++ } + } + + return 0; +@@ -901,7 +1074,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, +- struct trace_event_file *file) ++ struct trace_event_file *file, ++ bool remove) + { + 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, + return ERR_PTR(-ENOMEM); + + hist_data->attrs = attrs; ++ hist_data->remove = remove; + + ret = create_hist_fields(hist_data, file); + if (ret) +@@ -958,14 +1133,29 @@ static void hist_trigger_elt_update(stru + struct ring_buffer_event *rbe) + { + struct hist_field *hist_field; +- unsigned int i; ++ unsigned int i, var_idx; + u64 hist_val; + + for_each_hist_val_field(i, hist_data) { + hist_field = hist_data->fields[i]; +- hist_val = hist_field->fn(hist_field, rec, rbe); ++ hist_val = hist_field->fn(hist_field, 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); ++ if (hist_field->flags & HIST_FIELD_FL_VAR_ONLY) ++ continue; ++ } + tracing_map_update_sum(elt, i, hist_val); + } ++ ++ 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); ++ var_idx = hist_field->var.idx; ++ tracing_map_set_var(elt, var_idx, hist_val); ++ } ++ } + } + + static inline void add_to_key(char *compound_key, void *key, +@@ -1140,6 +1330,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); + ++ if (hist_data->fields[i]->flags & HIST_FIELD_FL_VAR) ++ continue; ++ + 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_ + { + const char *field_name = hist_field_name(hist_field, 0); + ++ if (hist_field->var.name) ++ seq_printf(m, "%s=", hist_field->var.name); ++ + 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 + struct event_trigger_data *data) + { + struct hist_trigger_data *hist_data = data->private_data; +- struct hist_field *key_field; ++ bool have_var_only = false; ++ struct hist_field *field; + unsigned int i; + + seq_puts(m, "hist:"); +@@ -1292,25 +1489,47 @@ static int event_hist_trigger_print(stru + seq_puts(m, "keys="); + + for_each_hist_key_field(i, hist_data) { +- key_field = hist_data->fields[i]; ++ field = hist_data->fields[i]; + + if (i > hist_data->n_vals) + seq_puts(m, ","); + +- if (key_field->flags & HIST_FIELD_FL_STACKTRACE) ++ if (field->flags & HIST_FIELD_FL_STACKTRACE) + seq_puts(m, "stacktrace"); + else +- hist_field_print(m, key_field); ++ hist_field_print(m, field); + } + + seq_puts(m, ":vals="); + + for_each_hist_val_field(i, hist_data) { ++ field = hist_data->fields[i]; ++ if (field->flags & HIST_FIELD_FL_VAR_ONLY) { ++ have_var_only = true; ++ continue; ++ } ++ + if (i == HITCOUNT_IDX) + seq_puts(m, "hitcount"); + else { + seq_puts(m, ","); +- hist_field_print(m, hist_data->fields[i]); ++ hist_field_print(m, field); ++ } ++ } ++ ++ if (have_var_only) { ++ unsigned int n = 0; ++ ++ seq_puts(m, ":"); ++ ++ for_each_hist_val_field(i, hist_data) { ++ field = hist_data->fields[i]; ++ ++ if (field->flags & HIST_FIELD_FL_VAR_ONLY) { ++ if (n++) ++ seq_puts(m, ","); ++ hist_field_print(m, field); ++ } + } + } + +@@ -1318,7 +1537,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; +- unsigned int idx; ++ unsigned int idx, first_key_idx; ++ ++ /* skip VAR_ONLY vals */ ++ first_key_idx = hist_data->n_vals - hist_data->n_var_only; + + sort_key = &hist_data->sort_keys[i]; + idx = sort_key->field_idx; +@@ -1331,8 +1553,11 @@ static int event_hist_trigger_print(stru + + if (idx == HITCOUNT_IDX) + seq_puts(m, "hitcount"); +- else ++ else { ++ if (idx >= first_key_idx) ++ idx += hist_data->n_var_only; + hist_field_print(m, hist_data->fields[idx]); ++ } + + if (sort_key->descending) + seq_puts(m, ".descending"); +@@ -1656,12 +1881,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; ++ bool remove = false; + char *trigger; + int ret = 0; + + if (!param) + return -EINVAL; + ++ if (glob[0] == '!') ++ remove = true; ++ + /* 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 + if (attrs->map_bits) + hist_trigger_bits = attrs->map_bits; + +- hist_data = create_hist_data(hist_trigger_bits, attrs, file); ++ hist_data = create_hist_data(hist_trigger_bits, attrs, file, remove); + 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 + goto out_free; + } + +- if (glob[0] == '!') { ++ if (remove) { + cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); + ret = 0; + goto out_free; diff --git a/patches/0017-cpu-hotplug-Use-stop_machine_cpuslocked-in-takedown_.patch b/patches/0017-cpu-hotplug-Use-stop_machine_cpuslocked-in-takedown_.patch index 80b204a203405..bbebf44fead85 100644 --- a/patches/0017-cpu-hotplug-Use-stop_machine_cpuslocked-in-takedown_.patch +++ b/patches/0017-cpu-hotplug-Use-stop_machine_cpuslocked-in-takedown_.patch @@ -1,4 +1,3 @@ -From 210e21331fc3a396af640cec652be769d146e49f Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Wed, 24 May 2017 10:15:28 +0200 Subject: [PATCH 17/32] cpu/hotplug: Use stop_machine_cpuslocked() in diff --git a/patches/0017-sched-core-Enable-might_sleep-and-smp_processor_id-c.patch b/patches/0017-sched-core-Enable-might_sleep-and-smp_processor_id-c.patch index 2ee03f835dde4..beda0b8213761 100644 --- a/patches/0017-sched-core-Enable-might_sleep-and-smp_processor_id-c.patch +++ b/patches/0017-sched-core-Enable-might_sleep-and-smp_processor_id-c.patch @@ -1,4 +1,3 @@ -From 1c3c5eab171590f86edd8d31389d61dd1efe3037 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 16 May 2017 20:42:48 +0200 Subject: [PATCH 17/17] sched/core: Enable might_sleep() and smp_processor_id() diff --git a/patches/0017-tracing-Account-for-variables-in-named-trigger-compa.patch b/patches/0017-tracing-Account-for-variables-in-named-trigger-compa.patch new file mode 100644 index 0000000000000..b55236edd88c1 --- /dev/null +++ b/patches/0017-tracing-Account-for-variables-in-named-trigger-compa.patch @@ -0,0 +1,42 @@ +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 + compatibility + +Named triggers must also have the same set of variables in order to be +considered compatible - update the trigger match test to account for +that. + +The reason for this requirement is that named triggers with variables +are meant to allow one or more events to set the same variable. + +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 | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- 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 + sort_key = &hist_data->sort_keys[i]; + idx = sort_key->field_idx; + +- if (WARN_ON(idx >= TRACING_MAP_FIELDS_MAX)) ++ if (WARN_ON(idx >= HIST_FIELDS_MAX)) + return -EINVAL; + + if (i > 0) +@@ -1733,6 +1733,12 @@ static bool hist_trigger_match(struct ev + return false; + if (key_field->is_signed != key_field_test->is_signed) + return false; ++ if ((key_field->var.name && !key_field_test->var.name) || ++ (!key_field->var.name && key_field_test->var.name)) ++ return false; ++ if ((key_field->var.name && key_field_test->var.name) && ++ strcmp(key_field->var.name, key_field_test->var.name) != 0) ++ return false; + } + + for (i = 0; i < hist_data->n_sort_keys; i++) { diff --git a/patches/0018-tracing-Add-simple-expression-support-to-hist-trigge.patch b/patches/0018-tracing-Add-simple-expression-support-to-hist-trigge.patch new file mode 100644 index 0000000000000..dd2ae233312b3 --- /dev/null +++ b/patches/0018-tracing-Add-simple-expression-support-to-hist-trigge.patch @@ -0,0 +1,602 @@ +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 + +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 +order to support a minimal set of useful inter-event calculations. + +These operations are needed for calculating latencies between events +(timestamp1-timestamp0) and for combined latencies (latencies over 3 +or more events). + +In the process, factor out some common code from key and value +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(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -32,6 +32,13 @@ typedef u64 (*hist_field_fn_t) (struct h + #define HIST_FIELD_OPERANDS_MAX 2 + #define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX) + ++enum field_op_id { ++ FIELD_OP_NONE, ++ FIELD_OP_PLUS, ++ FIELD_OP_MINUS, ++ FIELD_OP_UNARY_MINUS, ++}; ++ + struct hist_var { + char *name; + struct hist_trigger_data *hist_data; +@@ -48,6 +55,8 @@ struct hist_field { + struct hist_field *operands[HIST_FIELD_OPERANDS_MAX]; + struct hist_trigger_data *hist_data; + struct hist_var var; ++ enum field_op_id operator; ++ char *name; + }; + + static u64 hist_field_none(struct hist_field *field, void *event, +@@ -98,6 +107,41 @@ static u64 hist_field_log2(struct hist_f + 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) ++{ ++ 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); ++ ++ return val1 + val2; ++} ++ ++static u64 hist_field_minus(struct hist_field *hist_field, void *event, ++ struct ring_buffer_event *rbe) ++{ ++ 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); ++ ++ return val1 - val2; ++} ++ ++static u64 hist_field_unary_minus(struct hist_field *hist_field, void *event, ++ struct ring_buffer_event *rbe) ++{ ++ struct hist_field *operand = hist_field->operands[0]; ++ ++ s64 sval = (s64)operand->fn(operand, event, rbe); ++ u64 val = (u64)-sval; ++ ++ return val; ++} ++ + #define DEFINE_HIST_FIELD_FN(type) \ + static u64 hist_field_##type(struct hist_field *hist_field, \ + void *event, \ +@@ -148,6 +192,7 @@ enum hist_field_flags { + HIST_FIELD_FL_TIMESTAMP_USECS = 2048, + HIST_FIELD_FL_VAR = 4096, + HIST_FIELD_FL_VAR_ONLY = 8192, ++ HIST_FIELD_FL_EXPR = 16384, + }; + + struct hist_trigger_attrs { +@@ -210,6 +255,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"; ++ else if (field->flags & HIST_FIELD_FL_EXPR) ++ field_name = field->name; + + if (field_name == NULL) + field_name = ""; +@@ -444,6 +491,73 @@ static const struct tracing_map_ops hist + .elt_init = hist_trigger_elt_comm_init, + }; + ++static char *expr_str(struct hist_field *field, unsigned int level) ++{ ++ char *expr = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL); ++ ++ if (!expr || level > 1) ++ return NULL; ++ ++ if (field->operator == FIELD_OP_UNARY_MINUS) { ++ char *subexpr; ++ ++ strcat(expr, "-("); ++ subexpr = expr_str(field->operands[0], ++level); ++ if (!subexpr) { ++ kfree(expr); ++ return NULL; ++ } ++ strcat(expr, subexpr); ++ strcat(expr, ")"); ++ ++ return expr; ++ } ++ ++ strcat(expr, hist_field_name(field->operands[0], 0)); ++ ++ switch (field->operator) { ++ case FIELD_OP_MINUS: ++ strcat(expr, "-"); ++ break; ++ case FIELD_OP_PLUS: ++ strcat(expr, "+"); ++ break; ++ default: ++ kfree(expr); ++ return NULL; ++ } ++ ++ strcat(expr, hist_field_name(field->operands[1], 0)); ++ ++ return expr; ++} ++ ++static int contains_operator(char *str) ++{ ++ enum field_op_id field_op = FIELD_OP_NONE; ++ char *op; ++ ++ op = strpbrk(str, "+-"); ++ if (!op) ++ return FIELD_OP_NONE; ++ ++ switch (*op) { ++ case '-': ++ if (*str == '-') ++ field_op = FIELD_OP_UNARY_MINUS; ++ else ++ field_op = FIELD_OP_MINUS; ++ break; ++ case '+': ++ field_op = FIELD_OP_PLUS; ++ break; ++ default: ++ break; ++ } ++ ++ return field_op; ++} ++ + 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); + + kfree(hist_field->var.name); ++ kfree(hist_field->name); + + kfree(hist_field); + } +@@ -479,6 +594,9 @@ static struct hist_field *create_hist_fi + + hist_field->hist_data = hist_data; + ++ if (flags & HIST_FIELD_FL_EXPR) ++ goto out; /* caller will populate */ ++ + 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 + } + } + ++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; ++ ++ field_name = strsep(&field_str, "."); ++ if (field_str) { ++ if (strcmp(field_str, "hex") == 0) ++ *flags |= HIST_FIELD_FL_HEX; ++ else if (strcmp(field_str, "sym") == 0) ++ *flags |= HIST_FIELD_FL_SYM; ++ else if (strcmp(field_str, "sym-offset") == 0) ++ *flags |= HIST_FIELD_FL_SYM_OFFSET; ++ else if ((strcmp(field_str, "execname") == 0) && ++ (strcmp(field_name, "common_pid") == 0)) ++ *flags |= HIST_FIELD_FL_EXECNAME; ++ else if (strcmp(field_str, "syscall") == 0) ++ *flags |= HIST_FIELD_FL_SYSCALL; ++ else if (strcmp(field_str, "log2") == 0) ++ *flags |= HIST_FIELD_FL_LOG2; ++ else if (strcmp(field_str, "usecs") == 0) ++ *flags |= HIST_FIELD_FL_TIMESTAMP_USECS; ++ else ++ return ERR_PTR(-EINVAL); ++ } ++ ++ if (strcmp(field_name, "$common_timestamp") == 0) { ++ *flags |= HIST_FIELD_FL_TIMESTAMP; ++ hist_data->enable_timestamps = true; ++ if (*flags & HIST_FIELD_FL_TIMESTAMP_USECS) ++ hist_data->attrs->ts_in_usecs = true; ++ } else { ++ field = trace_find_event_field(file->event_call, field_name); ++ if (!field) ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return field; ++} ++ ++struct hist_field *parse_atom(struct hist_trigger_data *hist_data, ++ struct trace_event_file *file, char *str, ++ unsigned long *flags, char *var_name) ++{ ++ struct ftrace_event_field *field = NULL; ++ struct hist_field *hist_field = NULL; ++ int ret = 0; ++ ++ field = parse_field(hist_data, file, str, flags); ++ if (IS_ERR(field)) { ++ ret = PTR_ERR(field); ++ goto out; ++ } ++ ++ hist_field = create_hist_field(hist_data, field, *flags, var_name); ++ if (!hist_field) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ return hist_field; ++ out: ++ return ERR_PTR(ret); ++} ++ ++static struct hist_field *parse_expr(struct hist_trigger_data *hist_data, ++ struct trace_event_file *file, ++ char *str, unsigned long flags, ++ char *var_name, unsigned int level); ++ ++static struct hist_field *parse_unary(struct hist_trigger_data *hist_data, ++ struct trace_event_file *file, ++ char *str, unsigned long flags, ++ char *var_name, unsigned int level) ++{ ++ struct hist_field *operand1, *expr = NULL; ++ unsigned long operand_flags; ++ char *operand1_str; ++ int ret = 0; ++ char *s; ++ ++ // we support only -(xxx) i.e. explicit parens required ++ ++ if (level > 2) { ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ str++; // skip leading '-' ++ ++ s = strchr(str, '('); ++ if (s) ++ str++; ++ else { ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ s = strchr(str, ')'); ++ if (s) ++ *s = '\0'; ++ else { ++ ret = -EINVAL; // no closing ')' ++ goto free; ++ } ++ ++ operand1_str = strsep(&str, "("); ++ if (!operand1_str) ++ goto free; ++ ++ flags |= HIST_FIELD_FL_EXPR; ++ expr = create_hist_field(hist_data, NULL, flags, var_name); ++ if (!expr) { ++ ret = -ENOMEM; ++ goto free; ++ } ++ ++ operand_flags = 0; ++ operand1 = parse_expr(hist_data, file, str, operand_flags, NULL, ++level); ++ if (IS_ERR(operand1)) { ++ ret = PTR_ERR(operand1); ++ 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; ++ expr->name = expr_str(expr, 0); ++ ++ return expr; ++ free: ++ return ERR_PTR(ret); ++} ++ ++static struct hist_field *parse_expr(struct hist_trigger_data *hist_data, ++ struct trace_event_file *file, ++ char *str, unsigned long flags, ++ char *var_name, unsigned int level) ++{ ++ struct hist_field *operand1 = NULL, *operand2 = NULL, *expr = NULL; ++ unsigned long operand_flags; ++ int field_op, ret = -EINVAL; ++ char *sep, *operand1_str; ++ ++ if (level > 2) ++ return NULL; ++ ++ field_op = contains_operator(str); ++ if (field_op == FIELD_OP_NONE) ++ return NULL; ++ ++ if (field_op == FIELD_OP_UNARY_MINUS) ++ return parse_unary(hist_data, file, str, flags, var_name, ++level); ++ ++ switch (field_op) { ++ case FIELD_OP_MINUS: ++ sep = "-"; ++ break; ++ case FIELD_OP_PLUS: ++ sep = "+"; ++ break; ++ default: ++ goto free; ++ } ++ ++ operand1_str = strsep(&str, sep); ++ if (!operand1_str || !str) ++ goto free; ++ ++ operand_flags = 0; ++ operand1 = parse_atom(hist_data, file, operand1_str, ++ &operand_flags, NULL); ++ if (IS_ERR(operand1)) { ++ ret = PTR_ERR(operand1); ++ operand1 = NULL; ++ goto free; ++ } ++ ++ // rest of string could be another expression e.g. b+c in a+b+c ++ operand_flags = 0; ++ operand2 = parse_expr(hist_data, file, str, operand_flags, NULL, ++level); ++ if (IS_ERR(operand2)) { ++ ret = PTR_ERR(operand2); ++ 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; ++ } ++ } ++ ++ flags |= HIST_FIELD_FL_EXPR; ++ expr = create_hist_field(hist_data, NULL, flags, var_name); ++ if (!expr) { ++ ret = -ENOMEM; ++ goto free; ++ } ++ ++ expr->operands[0] = operand1; ++ expr->operands[1] = operand2; ++ expr->operator = field_op; ++ expr->name = expr_str(expr, 0); ++ ++ switch (field_op) { ++ case FIELD_OP_MINUS: ++ expr->fn = hist_field_minus; ++ break; ++ case FIELD_OP_PLUS: ++ expr->fn = hist_field_plus; ++ break; ++ default: ++ goto free; ++ } ++ ++ return expr; ++ free: ++ destroy_hist_field(operand1, 0); ++ destroy_hist_field(operand2, 0); ++ destroy_hist_field(expr, 0); ++ ++ return ERR_PTR(ret); ++} ++ + 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) + { +- struct ftrace_event_field *field = NULL; +- char *field_name, *var_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) +- flags |= HIST_FIELD_FL_HEX; +- else { +- 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) { +- 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; + } + } + +- 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_data->n_vals; + ++hist_data->n_fields; + +- if (hist_data->fields[val_idx]->flags & HIST_FIELD_FL_VAR_ONLY) ++ if (hist_field->flags & HIST_FIELD_FL_VAR_ONLY) + 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_ + struct trace_event_file *file, + 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; +@@ -754,60 +1103,33 @@ 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); + } else { +- char *field_name = strsep(&field_str, "."); +- +- if (field_str) { +- if (strcmp(field_str, "hex") == 0) +- flags |= HIST_FIELD_FL_HEX; +- else if (strcmp(field_str, "sym") == 0) +- flags |= HIST_FIELD_FL_SYM; +- else if (strcmp(field_str, "sym-offset") == 0) +- flags |= HIST_FIELD_FL_SYM_OFFSET; +- else if ((strcmp(field_str, "execname") == 0) && +- (strcmp(field_name, "common_pid") == 0)) +- flags |= HIST_FIELD_FL_EXECNAME; +- else if (strcmp(field_str, "syscall") == 0) +- flags |= HIST_FIELD_FL_SYSCALL; +- else if (strcmp(field_str, "log2") == 0) +- flags |= HIST_FIELD_FL_LOG2; +- else if (strcmp(field_str, "usecs") == 0) +- flags |= HIST_FIELD_FL_TIMESTAMP_USECS; +- else { +- 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; +- if (flags & HIST_FIELD_FL_TIMESTAMP_USECS) +- hist_data->attrs->ts_in_usecs = true; +- key_size = sizeof(u64); +- } else { +- field = trace_find_event_field(file->event_call, field_name); +- if (!field) { +- 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; + } +- +- if (is_string_field(field)) +- key_size = MAX_FILTER_STR_VAL; +- else +- key_size = field->size; + } +- } + +- hist_data->fields[key_idx] = create_hist_field(hist_data, field, flags, var_name); +- 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; ++ + hist_data->key_size += key_size; ++ + if (hist_data->key_size > HIST_KEY_SIZE_MAX) { + ret = -EINVAL; + goto out; +@@ -1330,7 +1652,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); + +- if (hist_data->fields[i]->flags & HIST_FIELD_FL_VAR) ++ if (hist_data->fields[i]->flags & HIST_FIELD_FL_VAR || ++ hist_data->fields[i]->flags & HIST_FIELD_FL_EXPR) + continue; + + if (hist_data->fields[i]->flags & HIST_FIELD_FL_HEX) { diff --git a/patches/0018-x86-perf-Drop-EXPORT-of-perf_check_microcode.patch b/patches/0018-x86-perf-Drop-EXPORT-of-perf_check_microcode.patch index d53294f11b5d6..86d24cbcd049a 100644 --- a/patches/0018-x86-perf-Drop-EXPORT-of-perf_check_microcode.patch +++ b/patches/0018-x86-perf-Drop-EXPORT-of-perf_check_microcode.patch @@ -1,4 +1,3 @@ -From 27d3b157fee0bad264eb745d5c547e2e0676f1a2 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:29 +0200 Subject: [PATCH 18/32] x86/perf: Drop EXPORT of perf_check_microcode diff --git a/patches/0019-perf-x86-intel-Drop-get_online_cpus-in-intel_snb_che.patch b/patches/0019-perf-x86-intel-Drop-get_online_cpus-in-intel_snb_che.patch index ae066c92721af..f5d1c72c5fe86 100644 --- a/patches/0019-perf-x86-intel-Drop-get_online_cpus-in-intel_snb_che.patch +++ b/patches/0019-perf-x86-intel-Drop-get_online_cpus-in-intel_snb_che.patch @@ -1,4 +1,3 @@ -From 1ba143a5216fb148211160a0ecc1f8d3f92f06bb Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Wed, 24 May 2017 10:15:30 +0200 Subject: [PATCH 19/32] perf/x86/intel: Drop get_online_cpus() in diff --git a/patches/0019-tracing-Add-variable-reference-handling-to-hist-trig.patch b/patches/0019-tracing-Add-variable-reference-handling-to-hist-trig.patch new file mode 100644 index 0000000000000..558d2c2ffd0b1 --- /dev/null +++ b/patches/0019-tracing-Add-variable-reference-handling-to-hist-trig.patch @@ -0,0 +1,1122 @@ +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 + triggers + +Add the necessary infrastructure to allow the variables defined on one +event to be referenced in another. This allows variables set by a +previous event to be referenced and used in expressions combining the +variable values saved by that previous event and the event fields of +the current event. For example, here's how a latency can be +calculated and saved into yet another variable named 'wakeup_lat': + + # echo 'hist:keys=pid,prio:ts0=common_timestamp ... + # echo 'hist:keys=next_pid:wakeup_lat=common_timestamp-$ts0 ... + +In the first event, the event's timetamp is saved into the variable +ts0. In the next line, ts0 is subtracted from the second event's +timestamp to produce the latency. + +Further users of variable references will be described in subsequent +patches, such as for instance how the 'wakeup_lat' variable above can +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_events_trigger.c | 6 + 3 files changed, 604 insertions(+), 123 deletions(-) + +--- a/kernel/trace/trace.h ++++ b/kernel/trace/trace.h +@@ -1448,6 +1448,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); ++extern struct event_trigger_data * ++get_named_trigger_data(struct event_trigger_data *data); + extern int register_event_command(struct event_command *cmd); + extern int unregister_event_command(struct event_command *cmd); + 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 { + struct hist_var var; + enum field_op_id operator; + char *name; ++ unsigned int var_idx; ++ unsigned int var_ref_idx; ++ 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 { + 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 tracing_map *map; + bool enable_timestamps; + bool remove; ++ struct hist_field *var_refs[TRACING_MAP_VARS_MAX]; ++ 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 + 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) ++{ ++ struct hist_field *found = NULL; ++ ++ if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR_REF) { ++ if (hist_field->var.idx == var_idx && ++ hist_field->var.hist_data == var_data) { ++ found = hist_field; ++ } ++ } ++ ++ return found; ++} ++ ++static struct hist_field *find_var_ref(struct hist_trigger_data *hist_data, ++ struct hist_trigger_data *var_data, ++ unsigned int var_idx) ++{ ++ struct hist_field *hist_field, *found = NULL; ++ unsigned int i, j; ++ ++ for_each_hist_field(i, hist_data) { ++ hist_field = hist_data->fields[i]; ++ found = check_var_ref(hist_field, var_data, var_idx); ++ if (found) ++ return found; ++ ++ for (j = 0; j < HIST_FIELD_OPERANDS_MAX; j++) { ++ struct hist_field *operand; ++ ++ operand = hist_field->operands[j]; ++ found = check_var_ref(operand, var_data, var_idx); ++ if (found) ++ return found; ++ } ++ } ++ ++ return found; ++} ++ ++static struct hist_field *find_any_var_ref(struct hist_trigger_data *hist_data, ++ unsigned int var_idx) ++{ ++ struct hist_field *found = NULL; ++ struct hist_var_data *var_data; ++ ++ list_for_each_entry(var_data, &hist_var_list, list) { ++ found = find_var_ref(var_data->hist_data, hist_data, var_idx); ++ if (found) ++ break; ++ } ++ ++ return found; ++} ++ ++static bool check_var_refs(struct hist_trigger_data *hist_data) ++{ ++ struct hist_field *field; ++ bool found = false; ++ int i; ++ ++ for_each_hist_field(i, hist_data) { ++ field = hist_data->fields[i]; ++ if (field && field->flags & HIST_FIELD_FL_VAR) { ++ if (find_any_var_ref(hist_data, field->var.idx)) { ++ found = true; ++ break; ++ } ++ } ++ } ++ ++ return found; ++} ++ ++static struct hist_var_data *find_hist_vars(struct hist_trigger_data *hist_data) ++{ ++ struct hist_var_data *var_data, *found = NULL; ++ ++ list_for_each_entry(var_data, &hist_var_list, list) { ++ if (var_data->hist_data == hist_data) { ++ found = var_data; ++ break; ++ } ++ } ++ ++ return found; ++} ++ ++static bool has_hist_vars(struct hist_trigger_data *hist_data) ++{ ++ struct hist_field *hist_field; ++ bool found = false; ++ int i; ++ ++ 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; ++ } ++ } ++ ++ return found; ++} ++ ++static int save_hist_vars(struct hist_trigger_data *hist_data) ++{ ++ struct hist_var_data *var_data; ++ ++ var_data = find_hist_vars(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_var_list); ++ ++ return 0; ++} ++ ++static void remove_hist_vars(struct hist_trigger_data *hist_data) ++{ ++ struct hist_var_data *var_data; ++ ++ var_data = find_hist_vars(hist_data); ++ if (!var_data) ++ return; ++ ++ if (WARN_ON(check_var_refs(hist_data))) ++ return; ++ ++ list_del(&var_data->list); ++ ++ kfree(var_data); ++} ++ ++static struct hist_field *find_var_field(struct hist_trigger_data *hist_data, ++ const char *var_name) ++{ ++ struct hist_field *hist_field, *found = NULL; ++ int i; ++ ++ for_each_hist_field(i, hist_data) { ++ hist_field = hist_data->fields[i]; ++ if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR && ++ strcmp(hist_field->var.name, var_name) == 0) { ++ found = hist_field; ++ break; ++ } ++ } ++ ++ return found; ++} ++ ++static struct hist_field *find_var(struct trace_event_file *file, ++ const char *var_name) ++{ ++ struct hist_trigger_data *hist_data; ++ struct event_trigger_data *test; ++ struct hist_field *hist_field; ++ ++ list_for_each_entry_rcu(test, &file->triggers, list) { ++ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { ++ hist_data = test->private_data; ++ hist_field = find_var_field(hist_data, var_name); ++ if (hist_field) ++ return hist_field; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct trace_event_file *find_var_file(const char *system, ++ const char *event_name, ++ const char *var_name) ++{ ++ struct hist_trigger_data *var_hist_data; ++ struct hist_var_data *var_data; ++ struct trace_event_call *call; ++ struct trace_event_file *file; ++ const char *name; ++ ++ list_for_each_entry(var_data, &hist_var_list, list) { ++ var_hist_data = var_data->hist_data; ++ file = var_hist_data->event_file; ++ call = file->event_call; ++ name = trace_event_name(call); ++ ++ if (!system || !event_name) { ++ if (find_var(file, var_name)) ++ return file; ++ continue; ++ } ++ ++ if (strcmp(event_name, name) != 0) ++ continue; ++ if (strcmp(system, call->class->system) != 0) ++ continue; ++ ++ return file; ++ } ++ ++ return NULL; ++} ++ ++static struct hist_field *find_file_var(struct trace_event_file *file, ++ const char *var_name) ++{ ++ struct hist_trigger_data *test_data; ++ struct event_trigger_data *test; ++ struct hist_field *hist_field; ++ ++ list_for_each_entry_rcu(test, &file->triggers, list) { ++ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { ++ test_data = test->private_data; ++ hist_field = find_var_field(test_data, var_name); ++ if (hist_field) ++ return hist_field; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct hist_field *find_event_var(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); ++ if (!file) ++ return NULL; ++ ++ hist_field = find_file_var(file, var_name); ++ ++ return hist_field; ++} ++ ++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, ++ void *event) ++{ ++ struct hist_elt_data *elt_data; ++ u64 var_val = 0; ++ ++ elt_data = elt->private_data; ++ var_val = elt_data->var_ref_vals[hist_field->var_ref_idx]; ++ ++ return var_val; ++} ++ ++static bool resolve_var_refs(struct hist_trigger_data *hist_data, void *key, ++ u64 *var_ref_vals, bool self) ++{ ++ struct hist_trigger_data *var_data; ++ struct tracing_map_elt *var_elt; ++ struct hist_field *hist_field; ++ unsigned int i, var_idx; ++ bool resolved = true; ++ u64 var_val = 0; ++ ++ for (i = 0; i < hist_data->n_var_refs; i++) { ++ hist_field = hist_data->var_refs[i]; ++ var_idx = hist_field->var.idx; ++ var_data = hist_field->var.hist_data; ++ ++ if (var_data == NULL) { ++ resolved = false; ++ break; ++ } ++ ++ if ((self && var_data != hist_data) || ++ (!self && var_data == hist_data)) ++ continue; ++ ++ var_elt = tracing_map_lookup(var_data->map, key); ++ if (!var_elt) { ++ resolved = false; ++ break; ++ } ++ ++ if (!tracing_map_var_set(var_elt, var_idx)) { ++ resolved = false; ++ break; ++ } ++ ++ if (self || !hist_field->read_once) ++ var_val = tracing_map_read_var(var_elt, var_idx); ++ else ++ var_val = tracing_map_read_var_once(var_elt, var_idx); ++ ++ var_ref_vals[i] = var_val; ++ } ++ ++ return resolved; ++} ++ + static const char *hist_field_name(struct hist_field *field, + unsigned int level) + { +@@ -255,7 +602,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"; +- else if (field->flags & HIST_FIELD_FL_EXPR) ++ else if (field->flags & HIST_FIELD_FL_EXPR || ++ field->flags & HIST_FIELD_FL_VAR_REF) + 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 + 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 + 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 (flags & HIST_FIELD_FL_EXPR) + goto out; /* caller will populate */ + ++ if (flags & HIST_FIELD_FL_VAR_REF) { ++ hist_field->fn = hist_field_var_ref; ++ goto out; ++ } ++ + 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 + } + } + ++static struct hist_field *create_var_ref(struct hist_field *var_field) ++{ ++ unsigned long flags = HIST_FIELD_FL_VAR_REF; ++ struct hist_field *ref_field; ++ ++ 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) { ++ destroy_hist_field(ref_field, 0); ++ return NULL; ++ } ++ } ++ ++ return ref_field; ++} ++ ++static struct hist_field *parse_var_ref(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] != '$') ++ return NULL; ++ ++ var_name++; ++ ++ var_field = find_event_var(system, event_name, var_name); ++ if (var_field) ++ ref_field = create_var_ref(var_field); ++ ++ return ref_field; ++} ++ + 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 + struct trace_event_file *file, char *str, + unsigned long *flags, char *var_name) + { ++ char *s, *ref_system = NULL, *ref_event = NULL, *ref_var = str; + struct ftrace_event_field *field = NULL; + struct hist_field *hist_field = NULL; + int ret = 0; + ++ s = strchr(str, '.'); ++ if (s) { ++ s = strchr(++s, '.'); ++ if (s) { ++ ref_system = strsep(&str, "."); ++ ref_event = strsep(&str, "."); ++ ref_var = str; ++ } ++ } ++ ++ 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; ++ } ++ + 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 + goto free; + } + ++ operand1->read_once = true; ++ operand2->read_once = true; ++ + expr->operands[0] = operand1; + expr->operands[1] = operand2; + expr->operator = field_op; +@@ -926,43 +1354,6 @@ static int create_hitcount_val(struct hi + return 0; + } + +-static struct hist_field *find_var_field(struct hist_trigger_data *hist_data, +- const char *var_name) +-{ +- struct hist_field *hist_field, *found = NULL; +- int i; +- +- for_each_hist_field(i, hist_data) { +- hist_field = hist_data->fields[i]; +- if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR && +- strcmp(hist_field->var.name, var_name) == 0) { +- found = hist_field; +- break; +- } +- } +- +- return found; +-} +- +-static struct hist_field *find_var(struct trace_event_file *file, +- const char *var_name) +-{ +- struct hist_trigger_data *hist_data; +- struct event_trigger_data *test; +- struct hist_field *hist_field; +- +- list_for_each_entry_rcu(test, &file->triggers, list) { +- if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { +- hist_data = test->private_data; +- hist_field = find_var_field(hist_data, var_name); +- if (hist_field) +- return hist_field; +- } +- } +- +- 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_ + } + } + ++ if (hist_field->flags & HIST_FIELD_FL_VAR_REF) { ++ destroy_hist_field(hist_field, 0); ++ ret = -EINVAL; ++ goto out; ++ } ++ + 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; + +- 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); +@@ -1433,10 +1814,6 @@ create_hist_data(unsigned int map_bits, + if (ret) + goto free; + +- ret = tracing_map_init(hist_data->map); +- if (ret) +- goto free; +- + hist_data->event_file = file; + out: + return hist_data; +@@ -1452,15 +1829,20 @@ 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, +- struct ring_buffer_event *rbe) ++ struct ring_buffer_event *rbe, ++ u64 *var_ref_vals) + { ++ struct hist_elt_data *elt_data; + struct hist_field *hist_field; + unsigned int i, var_idx; + u64 hist_val; + ++ elt_data = elt->private_data; ++ elt_data->var_ref_vals = var_ref_vals; ++ + 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 + 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 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 + if (use_compound_key) + key = compound_key; + ++ if (hist_data->n_var_refs && ++ !resolve_var_refs(hist_data, key, var_ref_vals, false)) ++ return; ++ + elt = tracing_map_insert(hist_data->map, key); +- if (elt) +- hist_trigger_elt_update(hist_data, elt, rec, rbe); ++ if (!elt) ++ return; ++ ++ hist_trigger_elt_update(hist_data, elt, rec, rbe, var_ref_vals); + } + + 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 + field_name = hist_field_name(hist_data->fields[i], 0); + + if (hist_data->fields[i]->flags & HIST_FIELD_FL_VAR || +- hist_data->fields[i]->flags & HIST_FIELD_FL_EXPR) ++ hist_data->fields[i]->flags & HIST_FIELD_FL_EXPR || ++ hist_data->fields[i]->flags & HIST_FIELD_FL_VAR_REF) + continue; + + if (hist_data->fields[i]->flags & HIST_FIELD_FL_HEX) { +@@ -1925,7 +2316,11 @@ static void event_hist_trigger_free(stru + if (!data->ref) { + if (data->name) + del_named_trigger(data); ++ + trigger_data_free(data); ++ ++ remove_hist_vars(hist_data); ++ + destroy_hist_data(hist_data); + } + } +@@ -2139,23 +2534,55 @@ static int hist_register_trigger(char *g + goto out; + } + +- list_add_rcu(&data->list, &file->triggers); + ret++; + +- update_cond_flag(file); +- + if (hist_data->enable_timestamps) + tracing_set_time_stamp_abs(file->tr, true); ++ out: ++ return ret; ++} ++ ++static int hist_trigger_enable(struct event_trigger_data *data, ++ struct trace_event_file *file) ++{ ++ int ret = 0; ++ ++ list_add_rcu(&data->list, &file->triggers); ++ ++ update_cond_flag(file); + + if (trace_event_trigger_enable_disable(file, 1) < 0) { + list_del_rcu(&data->list); + update_cond_flag(file); + ret--; + } +- out: ++ + return ret; + } + ++static bool hist_trigger_check_refs(struct event_trigger_data *data, ++ struct trace_event_file *file) ++{ ++ struct hist_trigger_data *hist_data = data->private_data; ++ struct event_trigger_data *test, *named_data = NULL; ++ ++ if (hist_data->attrs->name) ++ named_data = find_named_trigger(hist_data->attrs->name); ++ ++ list_for_each_entry_rcu(test, &file->triggers, list) { ++ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) { ++ if (!hist_trigger_match(data, test, named_data, false)) ++ continue; ++ hist_data = test->private_data; ++ if (check_var_refs(hist_data)) ++ return true; ++ break; ++ } ++ } ++ ++ return false; ++} ++ + 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 + tracing_set_time_stamp_abs(file->tr, false); + } + ++static bool hist_file_check_refs(struct trace_event_file *file) ++{ ++ 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; ++ if (check_var_refs(hist_data)) ++ return true; ++ break; ++ } ++ } ++ ++ return false; ++} ++ + static void hist_unreg_all(struct trace_event_file *file) + { + struct event_trigger_data *test, *n; + ++ if (hist_file_check_refs(file)) ++ 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 + } + + if (remove) { ++ if (hist_trigger_check_refs(trigger_data, file)) { ++ ret = -EBUSY; ++ goto out_free; ++ } ++ + 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 + goto out_free; + } else if (ret < 0) + goto out_free; ++ ++ if (get_named_trigger_data(trigger_data)) ++ goto enable; ++ ++ if (has_hist_vars(hist_data)) ++ save_hist_vars(hist_data); ++ ++ ret = tracing_map_init(hist_data->map); ++ if (ret) ++ goto out_unreg; ++enable: ++ ret = hist_trigger_enable(trigger_data, file); ++ if (ret) ++ goto out_unreg; ++ + /* Just return zero, not the number of registered triggers */ + ret = 0; + out: + return ret; ++ out_unreg: ++ cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); + out_free: + if (cmd_ops->set_filter) + cmd_ops->set_filter(NULL, trigger_data, NULL); + ++ remove_hist_vars(hist_data); ++ + kfree(trigger_data); + + 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 + data->named_data = named_data; + } + ++struct event_trigger_data * ++get_named_trigger_data(struct event_trigger_data *data) ++{ ++ return data->named_data; ++} ++ + static void + traceon_trigger(struct event_trigger_data *data, void *rec, + struct ring_buffer_event *event) diff --git a/patches/0020-PCI-Use-cpu_hotplug_disable-instead-of-get_online_cp.patch b/patches/0020-PCI-Use-cpu_hotplug_disable-instead-of-get_online_cp.patch index d0c58c21e355d..8bd1a98ffd9b3 100644 --- a/patches/0020-PCI-Use-cpu_hotplug_disable-instead-of-get_online_cp.patch +++ b/patches/0020-PCI-Use-cpu_hotplug_disable-instead-of-get_online_cp.patch @@ -1,4 +1,3 @@ -From 1ddd45f8d76f0c15ec4e44073eeaaee6a806ee81 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:31 +0200 Subject: [PATCH 20/32] PCI: Use cpu_hotplug_disable() instead of diff --git a/patches/0020-tracing-Add-support-for-dynamic-tracepoints.patch b/patches/0020-tracing-Add-support-for-dynamic-tracepoints.patch new file mode 100644 index 0000000000000..d7bc190643da4 --- /dev/null +++ b/patches/0020-tracing-Add-support-for-dynamic-tracepoints.patch @@ -0,0 +1,195 @@ +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-PCI-Replace-the-racy-recursion-prevention.patch b/patches/0021-PCI-Replace-the-racy-recursion-prevention.patch index 9c53fb2474375..685769451f50d 100644 --- a/patches/0021-PCI-Replace-the-racy-recursion-prevention.patch +++ b/patches/0021-PCI-Replace-the-racy-recursion-prevention.patch @@ -1,4 +1,3 @@ -From 0b2c2a71e6f07fb67e6f72817d39910f64d2e258 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:32 +0200 Subject: [PATCH 21/32] PCI: Replace the racy recursion prevention diff --git a/patches/0021-tracing-Add-hist-trigger-action-hook.patch b/patches/0021-tracing-Add-hist-trigger-action-hook.patch new file mode 100644 index 0000000000000..e0913ac9216d8 --- /dev/null +++ b/patches/0021-tracing-Add-hist-trigger-action-hook.patch @@ -0,0 +1,227 @@ +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 + +Add a hook for executing extra actions whenever a histogram entry is +added or updated. + +The default 'action' when a hist entry is added to a histogram is to +update the set of values associated with it. Some applications may +want to perform additional actions at that point, such as generate +another event, or compare and save a maximum. + +Add a simple framework for doing that; specific actions will be +implemented on top of it in later patches. + +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 | 114 +++++++++++++++++++++++++++++++++++++-- + 1 file changed, 111 insertions(+), 3 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -33,6 +33,7 @@ typedef u64 (*hist_field_fn_t) (struct h + + #define HIST_FIELD_OPERANDS_MAX 2 + #define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX) ++#define HIST_ACTIONS_MAX 8 + + enum field_op_id { + FIELD_OP_NONE, +@@ -233,6 +234,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 hist_trigger_data { +@@ -252,6 +256,21 @@ struct hist_trigger_data { + bool remove; + struct hist_field *var_refs[TRACING_MAP_VARS_MAX]; + unsigned int n_var_refs; ++ ++ struct action_data *actions[HIST_ACTIONS_MAX]; ++ unsigned int n_actions; ++}; ++ ++struct action_data; ++ ++typedef void (*action_fn_t) (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 action_data { ++ action_fn_t fn; ++ unsigned int var_ref_idx; + }; + + static u64 hist_field_timestamp(struct hist_field *hist_field, +@@ -681,6 +700,9 @@ static void destroy_hist_trigger_attrs(s + for (i = 0; i < attrs->n_assignments; i++) + kfree(attrs->assignment_str[i]); + ++ for (i = 0; i < attrs->n_actions; i++) ++ kfree(attrs->action_str[i]); ++ + kfree(attrs->name); + kfree(attrs->sort_key_str); + kfree(attrs->keys_str); +@@ -688,6 +710,16 @@ static void destroy_hist_trigger_attrs(s + kfree(attrs); + } + ++static int parse_action(char *str, struct hist_trigger_attrs *attrs) ++{ ++ int ret = 0; ++ ++ if (attrs->n_actions >= HIST_ACTIONS_MAX) ++ return ret; ++ ++ return ret; ++} ++ + static int parse_assignment(char *str, struct hist_trigger_attrs *attrs) + { + int ret = 0; +@@ -755,8 +787,9 @@ static struct hist_trigger_attrs *parse_ + else if (strcmp(str, "clear") == 0) + attrs->clear = true; + else { +- ret = -EINVAL; +- goto free; ++ ret = parse_action(str, attrs); ++ if (ret) ++ goto free; + } + } + +@@ -1722,11 +1755,63 @@ static int create_sort_keys(struct hist_ + return ret; + } + ++static void destroy_actions(struct hist_trigger_data *hist_data) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < hist_data->n_actions; i++) { ++ struct action_data *data = hist_data->actions[i]; ++ ++ kfree(data); ++ } ++} ++ ++static int create_actions(struct hist_trigger_data *hist_data, ++ struct trace_event_file *file) ++{ ++ 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]; ++ } ++ ++ return ret; ++} ++ ++static void print_actions(struct seq_file *m, ++ struct hist_trigger_data *hist_data, ++ struct tracing_map_elt *elt) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < hist_data->n_actions; i++) { ++ struct action_data *data = hist_data->actions[i]; ++ } ++} ++ ++static void print_actions_spec(struct seq_file *m, ++ struct hist_trigger_data *hist_data) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < hist_data->n_actions; i++) { ++ struct action_data *data = hist_data->actions[i]; ++ } ++} ++ + static void destroy_hist_data(struct hist_trigger_data *hist_data) + { ++ if (!hist_data) ++ return; ++ + destroy_hist_trigger_attrs(hist_data->attrs); + destroy_hist_fields(hist_data); + tracing_map_destroy(hist_data->map); ++ ++ destroy_actions(hist_data); ++ + kfree(hist_data); + } + +@@ -1886,6 +1971,20 @@ static inline void add_to_key(char *comp + memcpy(compound_key + key_field->offset, key, size); + } + ++static void ++hist_trigger_actions(struct hist_trigger_data *hist_data, ++ struct tracing_map_elt *elt, void *rec, ++ struct ring_buffer_event *rbe, u64 *var_ref_vals) ++{ ++ struct action_data *data; ++ unsigned int i; ++ ++ for (i = 0; i < hist_data->n_actions; i++) { ++ data = hist_data->actions[i]; ++ data->fn(hist_data, elt, rec, rbe, data, var_ref_vals); ++ } ++} ++ + 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 + return; + + hist_trigger_elt_update(hist_data, elt, rec, rbe, var_ref_vals); ++ ++ if (resolve_var_refs(hist_data, key, var_ref_vals, true)) ++ hist_trigger_actions(hist_data, elt, rec, rbe, var_ref_vals); + } + + static void hist_trigger_stacktrace_print(struct seq_file *m, +@@ -2278,6 +2380,8 @@ static int event_hist_trigger_print(stru + } + seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits)); + ++ print_actions_spec(m, hist_data); ++ + if (data->filter_str) + seq_printf(m, " if %s", data->filter_str); + +@@ -2740,6 +2844,10 @@ static int event_hist_trigger_func(struc + if (has_hist_vars(hist_data)) + save_hist_vars(hist_data); + ++ ret = create_actions(hist_data, file); ++ if (ret) ++ goto out_unreg; ++ + ret = tracing_map_init(hist_data->map); + if (ret) + goto out_unreg; +@@ -2761,8 +2869,8 @@ static int event_hist_trigger_func(struc + remove_hist_vars(hist_data); + + kfree(trigger_data); +- + destroy_hist_data(hist_data); ++ + goto out; + } + diff --git a/patches/0022-ACPI-processor-Use-cpu_hotplug_disable-instead-of-ge.patch b/patches/0022-ACPI-processor-Use-cpu_hotplug_disable-instead-of-ge.patch index b68af8458c1ee..9ee9bf9c9dadb 100644 --- a/patches/0022-ACPI-processor-Use-cpu_hotplug_disable-instead-of-ge.patch +++ b/patches/0022-ACPI-processor-Use-cpu_hotplug_disable-instead-of-ge.patch @@ -1,4 +1,3 @@ -From fdaf0a51bad496289356d11d796095a293794b5f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:33 +0200 Subject: [PATCH 22/32] ACPI/processor: Use cpu_hotplug_disable() instead of diff --git a/patches/0022-tracing-Add-support-for-synthetic-events.patch b/patches/0022-tracing-Add-support-for-synthetic-events.patch new file mode 100644 index 0000000000000..24cf13a45be3f --- /dev/null +++ b/patches/0022-tracing-Add-support-for-synthetic-events.patch @@ -0,0 +1,821 @@ +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 + +Synthetic events are user-defined events generated from hist trigger +variables saved from one or more other events. + +To define a synthetic event, the user writes a simple specification +consisting of the name of the new event along with one or more +variables and their type(s), to the tracing/synthetic_events file. + +For instance, the following creates a new event named 'wakeup_latency' +with 3 fields: lat, pid, and prio: + + # echo 'wakeup_latency u64 lat; pid_t pid; int prio' >> \ + /sys/kernel/debug/tracing/synthetic_events + +Reading the tracing/synthetic_events file lists all the +currently-defined synthetic events, in this case the event we defined +above: + + # cat /sys/kernel/debug/tracing/synthetic_events + wakeup_latency u64 lat; pid_t pid; int prio + +At this point, the synthetic event is ready to use, and a histogram +can be defined using it: + + # echo 'hist:keys=pid,prio,lat.log2:sort=pid,lat' >> \ + /sys/kernel/debug/tracing/events/synthetic/wakeup_latency/trigger + +The new event is created under the tracing/events/synthetic/ directory +and looks and behaves just like any other event: + + # ls /sys/kernel/debug/tracing/events/synthetic/wakeup_latency + enable filter format hist id trigger + +Although a histogram can be defined for it, nothing will happen until +an action tracing that event via the trace_synth() function occurs. +The trace_synth() function is very similar to all the other trace_* +invocations spread throughout the kernel, except in this case the +trace_ function and its corresponding tracepoint isn't statically +generated but defined by the user at run-time. + +How this can be automatically hooked up via a hist trigger 'action' is +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(+) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -20,10 +20,14 @@ + #include <linux/slab.h> + #include <linux/stacktrace.h> + #include <linux/rculist.h> ++#include <linux/tracefs.h> + + #include "tracing_map.h" + #include "trace.h" + ++#define SYNTH_SYSTEM "synthetic" ++#define SYNTH_FIELDS_MAX 16 ++ + struct hist_field; + + typedef u64 (*hist_field_fn_t) (struct hist_field *field, +@@ -261,6 +265,23 @@ struct hist_trigger_data { + unsigned int n_actions; + }; + ++struct synth_field { ++ char *type; ++ char *name; ++ unsigned int size; ++ bool is_signed; ++}; ++ ++struct synth_event { ++ struct list_head list; ++ char *name; ++ struct synth_field **fields; ++ unsigned int n_fields; ++ struct trace_event_class class; ++ struct trace_event_call call; ++ struct tracepoint *tp; ++}; ++ + struct action_data; + + typedef void (*action_fn_t) (struct hist_trigger_data *hist_data, +@@ -273,6 +294,688 @@ struct action_data { + unsigned int var_ref_idx; + }; + ++static LIST_HEAD(synth_event_list); ++static DEFINE_MUTEX(synth_event_mutex); ++ ++struct synth_trace_event { ++ struct trace_entry ent; ++ int n_fields; ++ u64 fields[]; ++}; ++ ++static int synth_event_define_fields(struct trace_event_call *call) ++{ ++ struct synth_trace_event trace; ++ int offset = offsetof(typeof(trace), fields); ++ struct synth_event *event = call->data; ++ unsigned int i, size; ++ char *name, *type; ++ bool is_signed; ++ int ret = 0; ++ ++ for (i = 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); ++ } ++ ++ return ret; ++} ++ ++static enum print_line_t print_synth_event(struct trace_iterator *iter, ++ int flags, ++ struct trace_event *event) ++{ ++ struct trace_array *tr = iter->tr; ++ struct trace_seq *s = &iter->seq; ++ struct synth_trace_event *entry; ++ struct synth_event *se; ++ unsigned int i; ++ ++ 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++) { ++ if (trace_seq_has_overflowed(s)) ++ goto end; ++ ++ /* parameter types */ ++ if (tr->trace_flags & TRACE_ITER_VERBOSE) ++ trace_seq_printf(s, "%s ", "u64"); ++ ++ /* parameter values */ ++ trace_seq_printf(s, "%s=%llu%s", se->fields[i]->name, ++ entry->fields[i], ++ i == entry->n_fields - 1 ? "" : ", "); ++ } ++end: ++ trace_seq_putc(s, '\n'); ++ ++ return trace_handle_return(s); ++} ++ ++static struct trace_event_functions synth_event_funcs = { ++ .trace = print_synth_event ++}; ++ ++static notrace void trace_event_raw_event_synth(void *__data, ++ u64 *var_ref_vals, ++ unsigned int var_ref_idx) ++{ ++ 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; ++ ++ event = trace_file->event_call->data; ++ ++ if (trace_trigger_soft_disabled(trace_file)) ++ return; ++ ++ fields_size = event->n_fields * 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; i < event->n_fields; i++) ++ entry->fields[i] = var_ref_vals[var_ref_idx + i]; ++ ++ trace_event_buffer_commit(&fbuffer); ++} ++ ++static void free_synth_event_print_fmt(struct trace_event_call *call) ++{ ++ if (call) ++ kfree(call->print_fmt); ++} ++ ++static int __set_synth_event_print_fmt(struct synth_event *event, ++ char *buf, int len) ++{ ++ int pos = 0; ++ int i; ++ ++ /* When len=0, we just calculate the needed length */ ++#define LEN_OR_ZERO (len ? len - pos : 0) ++ ++ 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), ++ 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); ++ } ++ ++#undef LEN_OR_ZERO ++ ++ /* return the length of print_fmt */ ++ return pos; ++} ++ ++static int set_synth_event_print_fmt(struct trace_event_call *call) ++{ ++ struct synth_event *event = call->data; ++ char *print_fmt; ++ int len; ++ ++ /* First: called with 0 length to calculate the needed length */ ++ len = __set_synth_event_print_fmt(event, NULL, 0); ++ ++ print_fmt = kmalloc(len + 1, GFP_KERNEL); ++ if (!print_fmt) ++ return -ENOMEM; ++ ++ /* Second: actually write the @print_fmt */ ++ __set_synth_event_print_fmt(event, print_fmt, len + 1); ++ call->print_fmt = print_fmt; ++ ++ 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); ++ kfree(field->name); ++ 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) ++{ ++ struct synth_field *field; ++ int len, ret = 0; ++ char *array; ++ ++ if (field_type[0] == ';') ++ field_type++; ++ ++ len = strlen(field_name); ++ if (field_name[len - 1] == ';') ++ field_name[len - 1] = '\0'; ++ ++ field = kzalloc(sizeof(*field), GFP_KERNEL); ++ if (!field) ++ return ERR_PTR(-ENOMEM); ++ ++ len = strlen(field_type) + 1; ++ array = strchr(field_name, '['); ++ if (array) ++ len += strlen(array); ++ field->type = kzalloc(len, GFP_KERNEL); ++ if (!field->type) { ++ ret = -ENOMEM; ++ goto free; ++ } ++ strcat(field->type, field_type); ++ if (array) ++ strcat(field->type, array); ++ ++ field->size = synth_field_size(field->type); ++ if (!field->size) { ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ field->is_signed = synth_field_signed(field->type); ++ ++ field->name = kstrdup(field_name, GFP_KERNEL); ++ if (!field->name) { ++ ret = -ENOMEM; ++ goto free; ++ } ++ out: ++ return field; ++ free: ++ free_synth_field(field); ++ field = ERR_PTR(ret); ++ goto out; ++} ++ ++static void free_synth_tracepoint(struct tracepoint *tp) ++{ ++ if (!tp) ++ return; ++ ++ kfree(tp->name); ++ kfree(tp); ++} ++ ++static struct tracepoint *alloc_synth_tracepoint(char *name) ++{ ++ struct tracepoint *tp; ++ int ret = 0; ++ ++ tp = kzalloc(sizeof(*tp), GFP_KERNEL); ++ if (!tp) { ++ ret = -ENOMEM; ++ goto free; ++ } ++ ++ tp->name = kstrdup(name, GFP_KERNEL); ++ if (!tp->name) { ++ ret = -ENOMEM; ++ goto free; ++ } ++ ++ return tp; ++ free: ++ free_synth_tracepoint(tp); ++ ++ return ERR_PTR(ret); ++} ++ ++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; ++ void *__data; ++ ++ if (!(cpu_online(raw_smp_processor_id()))) ++ return; ++ ++ it_func_ptr = rcu_dereference_sched((tp)->funcs); ++ if (it_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); ++ } ++ } ++} ++ ++static struct synth_event *find_synth_event(const char *name) ++{ ++ struct synth_event *event; ++ ++ list_for_each_entry(event, &synth_event_list, list) { ++ if (strcmp(event->name, name) == 0) ++ return event; ++ } ++ ++ return NULL; ++} ++ ++static int register_synth_event(struct synth_event *event) ++{ ++ struct trace_event_call *call = &event->call; ++ int ret = 0; ++ ++ event->call.class = &event->class; ++ event->class.system = kstrdup(SYNTH_SYSTEM, GFP_KERNEL); ++ if (!event->class.system) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ event->tp = alloc_synth_tracepoint(event->name); ++ if (IS_ERR(event->tp)) { ++ ret = PTR_ERR(event->tp); ++ event->tp = NULL; ++ goto out; ++ } ++ ++ INIT_LIST_HEAD(&call->class->fields); ++ call->event.funcs = &synth_event_funcs; ++ call->class->define_fields = synth_event_define_fields; ++ ++ ret = register_trace_event(&call->event); ++ if (!ret) { ++ ret = -ENODEV; ++ goto out; ++ } ++ call->flags = TRACE_EVENT_FL_TRACEPOINT; ++ call->class->reg = dynamic_trace_event_reg; ++ call->class->probe = trace_event_raw_event_synth; ++ call->data = event; ++ call->tp = event->tp; ++ ret = trace_add_event_call(call); ++ if (ret) { ++ pr_warn("Failed to register synthetic event: %s\n", ++ trace_event_name(call)); ++ goto err; ++ } ++ ++ ret = set_synth_event_print_fmt(call); ++ if (ret < 0) { ++ trace_remove_event_call(call); ++ goto err; ++ } ++ out: ++ return ret; ++ err: ++ unregister_trace_event(&call->event); ++ goto out; ++} ++ ++static int unregister_synth_event(struct synth_event *event) ++{ ++ struct trace_event_call *call = &event->call; ++ int ret; ++ ++ ret = trace_remove_event_call(call); ++ if (ret) { ++ pr_warn("Failed to remove synthetic event: %s\n", ++ trace_event_name(call)); ++ free_synth_event_print_fmt(call); ++ unregister_trace_event(&call->event); ++ } ++ ++ return ret; ++} ++ ++static void remove_synth_event(struct synth_event *event) ++{ ++ unregister_synth_event(event); ++ list_del(&event->list); ++} ++ ++static int add_synth_event(struct synth_event *event) ++{ ++ int ret; ++ ++ ret = register_synth_event(event); ++ if (ret) ++ return ret; ++ ++ list_add(&event->list, &synth_event_list); ++ ++ return 0; ++} ++ ++static void free_synth_event(struct synth_event *event) ++{ ++ unsigned int i; ++ ++ if (!event) ++ return; ++ ++ for (i = 0; i < event->n_fields; i++) ++ free_synth_field(event->fields[i]); ++ ++ kfree(event->fields); ++ kfree(event->name); ++ kfree(event->class.system); ++ free_synth_tracepoint(event->tp); ++ free_synth_event_print_fmt(&event->call); ++ kfree(event); ++} ++ ++static struct synth_event *alloc_synth_event(char *event_name, int n_fields, ++ struct synth_field **fields) ++{ ++ struct synth_event *event; ++ unsigned int i; ++ ++ event = kzalloc(sizeof(*event), GFP_KERNEL); ++ if (!event) { ++ event = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ event->name = kstrdup(event_name, GFP_KERNEL); ++ if (!event->name) { ++ kfree(event); ++ event = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ event->fields = kcalloc(n_fields, sizeof(event->fields), GFP_KERNEL); ++ if (!event->fields) { ++ free_synth_event(event); ++ event = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ for (i = 0; i < n_fields; i++) ++ event->fields[i] = fields[i]; ++ ++ event->n_fields = n_fields; ++ out: ++ return event; ++} ++ ++static int create_synth_event(int argc, char **argv) ++{ ++ struct synth_field *fields[SYNTH_FIELDS_MAX]; ++ struct synth_event *event = NULL; ++ bool delete_event = false; ++ int i, n_fields = 0, ret = 0; ++ char *name; ++ ++ mutex_lock(&synth_event_mutex); ++ ++ /* ++ * Argument syntax: ++ * - Add synthetic event: <event_name> field[;field] ... ++ * - Remove synthetic event: !<event_name> field[;field] ... ++ * where 'field' = type field_name ++ */ ++ if (argc < 1) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ name = argv[0]; ++ if (name[0] == '!') { ++ delete_event = true; ++ name++; ++ } ++ ++ event = find_synth_event(name); ++ if (event) { ++ if (delete_event) { ++ remove_synth_event(event); ++ goto err; ++ } else ++ ret = -EEXIST; ++ goto out; ++ } else if (delete_event) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (argc < 2) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ for (i = 1; i < argc - 1; i++) { ++ if (strcmp(argv[i], ";") == 0) ++ continue; ++ if (n_fields == SYNTH_FIELDS_MAX) { ++ ret = -EINVAL; ++ goto out; ++ } ++ fields[n_fields] = parse_synth_field(argv[i], argv[i + 1]); ++ if (!fields[n_fields]) ++ goto err; ++ i++; n_fields++; ++ } ++ if (i < argc) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ event = alloc_synth_event(name, n_fields, fields); ++ if (IS_ERR(event)) { ++ ret = PTR_ERR(event); ++ event = NULL; ++ goto err; ++ } ++ ++ add_synth_event(event); ++ out: ++ mutex_unlock(&synth_event_mutex); ++ ++ return ret; ++ err: ++ for (i = 0; i < n_fields; i++) ++ free_synth_field(fields[i]); ++ free_synth_event(event); ++ ++ goto out; ++} ++ ++static int release_all_synth_events(void) ++{ ++ struct synth_event *event, *e; ++ int ret = 0; ++ ++ mutex_lock(&synth_event_mutex); ++ ++ list_for_each_entry_safe(event, e, &synth_event_list, list) { ++ remove_synth_event(event); ++ free_synth_event(event); ++ } ++ ++ mutex_unlock(&synth_event_mutex); ++ ++ return ret; ++} ++ ++ ++static void *synth_events_seq_start(struct seq_file *m, loff_t *pos) ++{ ++ mutex_lock(&synth_event_mutex); ++ ++ return seq_list_start(&synth_event_list, *pos); ++} ++ ++static void *synth_events_seq_next(struct seq_file *m, void *v, loff_t *pos) ++{ ++ return seq_list_next(v, &synth_event_list, pos); ++} ++ ++static void synth_events_seq_stop(struct seq_file *m, void *v) ++{ ++ mutex_unlock(&synth_event_mutex); ++} ++ ++static int synth_events_seq_show(struct seq_file *m, void *v) ++{ ++ struct synth_field *field; ++ struct synth_event *event = v; ++ unsigned int i; ++ ++ seq_printf(m, "%s\t", event->name); ++ ++ for (i = 0; i < event->n_fields; i++) { ++ field = event->fields[i]; ++ ++ /* parameter values */ ++ seq_printf(m, "%s %s%s", field->type, field->name, ++ i == event->n_fields - 1 ? "" : "; "); ++ } ++ ++ seq_putc(m, '\n'); ++ ++ return 0; ++} ++ ++static const struct seq_operations synth_events_seq_op = { ++ .start = synth_events_seq_start, ++ .next = synth_events_seq_next, ++ .stop = synth_events_seq_stop, ++ .show = synth_events_seq_show ++}; ++ ++static int synth_events_open(struct inode *inode, struct file *file) ++{ ++ int ret; ++ ++ if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { ++ ret = release_all_synth_events(); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return seq_open(file, &synth_events_seq_op); ++} ++ ++static ssize_t synth_events_write(struct file *file, ++ const char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ return trace_parse_run_command(file, buffer, count, ppos, ++ create_synth_event); ++} ++ ++static const struct file_operations synth_events_fops = { ++ .open = synth_events_open, ++ .write = synth_events_write, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release, ++}; ++ + 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 + + return ret; + } ++ ++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); ++ goto err; ++ } ++ ++ entry = tracefs_create_file("synthetic_events", 0644, d_tracer, ++ tr, &synth_events_fops); ++ if (!entry) { ++ err = -ENODEV; ++ goto err; ++ } ++ ++ return err; ++ err: ++ pr_warn("Could not create tracefs 'synthetic_events' entry\n"); ++ ++ return err; ++} ++ ++fs_initcall(trace_events_hist_init); diff --git a/patches/0023-perf-tracing-cpuhotplug-Fix-locking-order.patch b/patches/0023-perf-tracing-cpuhotplug-Fix-locking-order.patch index 1485ee274f74d..4860a01bd4fc4 100644 --- a/patches/0023-perf-tracing-cpuhotplug-Fix-locking-order.patch +++ b/patches/0023-perf-tracing-cpuhotplug-Fix-locking-order.patch @@ -1,4 +1,3 @@ -From a63fbed776c7124ce9f606234267c3c095b2680e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:34 +0200 Subject: [PATCH 23/32] perf/tracing/cpuhotplug: Fix locking order diff --git a/patches/0023-tracing-Add-onmatch-hist-trigger-action-support.patch b/patches/0023-tracing-Add-onmatch-hist-trigger-action-support.patch new file mode 100644 index 0000000000000..8aa665e08a25a --- /dev/null +++ b/patches/0023-tracing-Add-onmatch-hist-trigger-action-support.patch @@ -0,0 +1,1268 @@ +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/0024-jump_label-Reorder-hotplug-lock-and-jump_label_lock.patch b/patches/0024-jump_label-Reorder-hotplug-lock-and-jump_label_lock.patch index 4031d40914097..20e370565e5b2 100644 --- a/patches/0024-jump_label-Reorder-hotplug-lock-and-jump_label_lock.patch +++ b/patches/0024-jump_label-Reorder-hotplug-lock-and-jump_label_lock.patch @@ -1,4 +1,3 @@ -From f2545b2d4ce13e068897ef60ae64dffe215f4152 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:35 +0200 Subject: [PATCH 24/32] jump_label: Reorder hotplug lock and jump_label_lock diff --git a/patches/0024-tracing-Add-onmax-hist-trigger-action-support.patch b/patches/0024-tracing-Add-onmax-hist-trigger-action-support.patch new file mode 100644 index 0000000000000..3fe92f608abc7 --- /dev/null +++ b/patches/0024-tracing-Add-onmax-hist-trigger-action-support.patch @@ -0,0 +1,455 @@ +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 + +Add an 'onmax(var).save(field,...)' hist trigger action which is +invoked whenever an event exceeds the current maximum. + +The end result is that the trace event fields or variables specified +as the onmax.save() params will be saved if 'var' exceeds the current +maximum for that hist trigger entry. This allows context from the +event that exhibited the new maximum to be saved for later reference. +When the histogram is displayed, additional fields 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 corresponding +to the current pid, and when the scheduler switches back to that pid, +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 \ + if comm=="cyclictest"' >> \ + /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger + + # echo 'hist:keys=next_pid:\ + 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 + +When the histogram is displayed, the max value and the saved values +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 + + Totals: + Hits: 12970 + Entries: 2 + 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(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -282,6 +282,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; ++ ++ struct field_var *max_vars[SYNTH_FIELDS_MAX]; ++ unsigned int n_max_vars; ++ unsigned int n_max_var_str; + }; + + 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; + }; + + static LIST_HEAD(synth_event_list); +@@ -1493,7 +1503,8 @@ static int parse_action(char *str, struc + if (attrs->n_actions >= HIST_ACTIONS_MAX) + return ret; + +- if ((strncmp(str, "onmatch(", strlen("onmatch(")) == 0)) { ++ if ((strncmp(str, "onmatch(", strlen("onmatch(")) == 0) || ++ (strncmp(str, "onmax(", strlen("onmax(")) == 0)) { + 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 + struct hist_elt_data *private_data = elt->private_data; + unsigned int i, n_str; + +- 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++) + kfree(private_data->field_var_str[i]); +@@ -1647,7 +1658,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 + hist_data->n_field_vars, 0); + } + ++static void update_max_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->max_vars, ++ hist_data->n_max_vars, hist_data->n_field_var_str); ++} ++ + 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 + return create_field_var(hist_data, file, var_name); + } + ++static void onmax_print(struct seq_file *m, ++ struct hist_trigger_data *hist_data, ++ struct tracing_map_elt *elt, ++ struct action_data *data) ++{ ++ unsigned int i, save_var_idx, max_idx = data->max_var->var.idx; ++ ++ seq_printf(m, "\n\tmax: %10llu", tracing_map_read_var(elt, max_idx)); ++ ++ for (i = 0; i < hist_data->n_max_vars; i++) { ++ struct hist_field *save_val = hist_data->max_vars[i]->val; ++ struct hist_field *save_var = hist_data->max_vars[i]->var; ++ u64 val; ++ ++ save_var_idx = save_var->var.idx; ++ ++ 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, ++ (char *)(uintptr_t)(val)); ++ } else ++ seq_printf(m, " %s: %10llu", save_var->var.name, val); ++ } ++} ++ ++static void onmax_save(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) ++{ ++ unsigned int max_idx = data->max_var->var.idx; ++ unsigned int max_var_ref_idx = data->max_var_ref_idx; ++ ++ u64 var_val, max_val; ++ ++ var_val = var_ref_vals[max_var_ref_idx]; ++ max_val = tracing_map_read_var(elt, max_idx); ++ ++ if (var_val <= max_val) ++ return; ++ ++ tracing_map_set_var(elt, max_idx, var_val); ++ ++ update_max_vars(hist_data, elt, rbe, rec); ++} ++ ++static void onmax_destroy(struct action_data *data) ++{ ++ unsigned int i; ++ ++ destroy_hist_field(data->max_var, 0); ++ destroy_hist_field(data->onmax_var, 0); ++ ++ kfree(data->onmax_var_str); ++ kfree(data->onmax_fn_name); ++ ++ for (i = 0; i < data->n_params; i++) ++ kfree(data->params[i]); ++ ++ kfree(data); ++} ++ ++static int onmax_create(struct hist_trigger_data *hist_data, ++ struct action_data *data) ++{ ++ struct trace_event_call *call = hist_data->event_file->event_call; ++ struct trace_event_file *file = hist_data->event_file; ++ struct hist_field *var_field, *ref_field, *max_var; ++ unsigned int var_ref_idx = hist_data->n_var_refs; ++ struct field_var *field_var; ++ char *onmax_var_str, *param; ++ const char *event_name; ++ unsigned long flags; ++ unsigned int i; ++ int ret = 0; ++ ++ onmax_var_str = data->onmax_var_str; ++ if (onmax_var_str[0] != '$') ++ return -EINVAL; ++ onmax_var_str++; ++ ++ event_name = trace_event_name(call); ++ var_field = find_target_event_var(hist_data, NULL, NULL, onmax_var_str); ++ if (!var_field) ++ return -EINVAL; ++ ++ flags = HIST_FIELD_FL_VAR_REF; ++ ref_field = create_hist_field(hist_data, NULL, flags, NULL); ++ 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) { ++ 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->fn = onmax_save; ++ data->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; ++ ++ for (i = 0; i < data->n_params; i++) { ++ param = kstrdup(data->params[i], GFP_KERNEL); ++ if (!param) ++ goto out; ++ ++ field_var = create_target_field_var(hist_data, NULL, NULL, param); ++ if (IS_ERR(field_var)) { ++ ret = PTR_ERR(field_var); ++ kfree(param); ++ goto out; ++ } ++ ++ hist_data->max_vars[hist_data->n_max_vars++] = field_var; ++ if (field_var->val->flags & HIST_FIELD_FL_STRING) ++ hist_data->n_max_var_str++; ++ ++ kfree(param); ++ } ++ ++ hist_data->actions[hist_data->n_actions++] = data; ++ out: ++ return ret; ++} ++ ++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 action_data *onmax_parse(char *str) ++{ ++ char *onmax_fn_name, *onmax_var_str; ++ struct action_data *data; ++ int ret = -EINVAL; ++ ++ data = kzalloc(sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return ERR_PTR(-ENOMEM); ++ ++ onmax_var_str = strsep(&str, ")"); ++ if (!onmax_var_str || !str) ++ return ERR_PTR(-EINVAL); ++ data->onmax_var_str = kstrdup(onmax_var_str, GFP_KERNEL); ++ ++ strsep(&str, "."); ++ if (!str) ++ goto free; ++ ++ onmax_fn_name = strsep(&str, "("); ++ if (!onmax_fn_name || !str) ++ goto free; ++ ++ if (strncmp(onmax_fn_name, "save", strlen("save")) == 0) { ++ char *params = strsep(&str, ")"); ++ ++ if (!params) ++ goto free; ++ ++ ret = parse_action_params(params, data); ++ if (ret) ++ goto free; ++ } ++ data->onmax_fn_name = kstrdup(onmax_fn_name, GFP_KERNEL); ++ ++ if (!data->onmax_var_str || !data->onmax_fn_name) { ++ ret = -ENOMEM; ++ goto free; ++ } ++ out: ++ return data; ++ free: ++ onmax_destroy(data); ++ data = ERR_PTR(ret); ++ goto out; ++} ++ + static void onmatch_destroy(struct action_data *data) + { + unsigned int i; +@@ -2689,37 +2925,6 @@ static int check_synth_field(struct synt + 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) +@@ -3313,6 +3518,8 @@ static void destroy_actions(struct hist_ + + if (data->fn == action_trace) + onmatch_destroy(data); ++ else if (data->fn == onmax_save) ++ onmax_destroy(data); + else + kfree(data); + } +@@ -3341,6 +3548,18 @@ static int create_actions(struct hist_tr + onmatch_destroy(data); + return ret; + } ++ } else if (strncmp(str, "onmax(", strlen("onmax(")) == 0) { ++ char *action_str = str + strlen("onmax("); ++ ++ data = onmax_parse(action_str); ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ ++ ret = onmax_create(hist_data, data); ++ if (ret) { ++ onmax_destroy(data); ++ return ret; ++ } + } + } + +@@ -3355,9 +3574,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]; ++ ++ if (data->fn == onmax_save) ++ onmax_print(m, hist_data, elt, data); + } + } + ++static void print_onmax_spec(struct seq_file *m, ++ struct hist_trigger_data *hist_data, ++ struct action_data *data) ++{ ++ unsigned int i; ++ ++ seq_puts(m, ":onmax("); ++ 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); ++ if (i < hist_data->n_max_vars - 1) ++ seq_puts(m, ","); ++ } ++ seq_puts(m, ")"); ++} ++ + 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 + + if (data->fn == action_trace) + print_onmatch_spec(m, hist_data, data); ++ else if (data->fn == onmax_save) ++ print_onmax_spec(m, hist_data, data); + } + } + diff --git a/patches/0025-kprobes-Cure-hotplug-lock-ordering-issues.patch b/patches/0025-kprobes-Cure-hotplug-lock-ordering-issues.patch index 19126208a19ce..cd00bbe23c857 100644 --- a/patches/0025-kprobes-Cure-hotplug-lock-ordering-issues.patch +++ b/patches/0025-kprobes-Cure-hotplug-lock-ordering-issues.patch @@ -1,4 +1,3 @@ -From 2d1e38f56622b9bb5af85be63c1052c056f5c677 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:36 +0200 Subject: [PATCH 25/32] kprobes: Cure hotplug lock ordering issues diff --git a/patches/0025-tracing-Allow-whitespace-to-surround-hist-trigger-fi.patch b/patches/0025-tracing-Allow-whitespace-to-surround-hist-trigger-fi.patch new file mode 100644 index 0000000000000..9c88c398bcdfc --- /dev/null +++ b/patches/0025-tracing-Allow-whitespace-to-surround-hist-trigger-fi.patch @@ -0,0 +1,57 @@ +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 + filter + +The existing code only allows for one space before and after the 'if' +specifying the filter for a hist trigger. Add code to make that more +permissive as far as whitespace goes. + +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 | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +--- 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; + bool remove = false; +- char *trigger; ++ char *trigger, *p; + int ret = 0; + + if (!param) +@@ -4642,9 +4642,19 @@ static int event_hist_trigger_func(struc + remove = true; + + /* separate the trigger from the filter (k:v [if filter]) */ +- trigger = strsep(¶m, " \t"); +- if (!trigger) +- return -EINVAL; ++ trigger = param; ++ p = strstr(param, " if"); ++ if (!p) ++ p = strstr(param, "\tif"); ++ if (p) { ++ if (p == trigger) ++ return -EINVAL; ++ param = p + 1; ++ param = strstrip(param); ++ *p = '\0'; ++ trigger = strstrip(trigger); ++ } else ++ param = NULL; + + attrs = parse_hist_trigger_attrs(trigger); + if (IS_ERR(attrs)) +@@ -4694,6 +4704,7 @@ static int event_hist_trigger_func(struc + } + + ret = cmd_ops->reg(glob, trigger_ops, trigger_data, file); ++ + /* + * The above returns on success the # of triggers registered, + * but if it didn't register any it returns zero. Consider no diff --git a/patches/0026-arm64-Prevent-cpu-hotplug-rwsem-recursion.patch b/patches/0026-arm64-Prevent-cpu-hotplug-rwsem-recursion.patch index 7f75a4abaa0bd..012d8e0915fc6 100644 --- a/patches/0026-arm64-Prevent-cpu-hotplug-rwsem-recursion.patch +++ b/patches/0026-arm64-Prevent-cpu-hotplug-rwsem-recursion.patch @@ -1,4 +1,3 @@ -From c23a465625e287c4deba0fdf5e8adc59cfd2a0b7 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:37 +0200 Subject: [PATCH 26/32] arm64: Prevent cpu hotplug rwsem recursion 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 new file mode 100644 index 0000000000000..aeaca9d7c9b2a --- /dev/null +++ b/patches/0026-tracing-Make-duplicate-count-from-tracing_map-availa.patch @@ -0,0 +1,124 @@ +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/0027-arm-Prevent-hotplug-rwsem-recursion.patch b/patches/0027-arm-Prevent-hotplug-rwsem-recursion.patch index 0e61074ae428d..ac0e3a83ca316 100644 --- a/patches/0027-arm-Prevent-hotplug-rwsem-recursion.patch +++ b/patches/0027-arm-Prevent-hotplug-rwsem-recursion.patch @@ -1,4 +1,3 @@ -From 9489cc8f370be811f7e741a772bcce88b712272d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:38 +0200 Subject: [PATCH 27/32] arm: Prevent hotplug rwsem recursion diff --git a/patches/0027-tracing-Add-cpu-field-for-hist-triggers.patch b/patches/0027-tracing-Add-cpu-field-for-hist-triggers.patch new file mode 100644 index 0000000000000..3312a4c534024 --- /dev/null +++ b/patches/0027-tracing-Add-cpu-field-for-hist-triggers.patch @@ -0,0 +1,132 @@ +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 + +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 +than $cpu or $common_cpu because 'cpu' already exists as a special +filter field and it makes more sense to match that rather than add +another name for the same thing. + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + Documentation/trace/events.txt | 18 ++++++++++++++++++ + kernel/trace/trace_events_hist.c | 30 +++++++++++++++++++++++++++--- + 2 files changed, 45 insertions(+), 3 deletions(-) + +--- a/Documentation/trace/events.txt ++++ b/Documentation/trace/events.txt +@@ -668,6 +668,24 @@ triggers (you have to use '!' for each o + The examples below provide a more concrete illustration of the + concepts and typical usage patterns discussed above. + ++ 'synthetic' event fields ++ ------------------------ ++ ++ There are a number of 'synthetic fields' available for use as keys ++ or values in a hist trigger. These look like and behave as if they ++ were event fields, but aren't actually part of the event's field ++ definition or format file. They are however available for any ++ event, and can be used anywhere an actual event field could be. ++ 'Synthetic' field names are always prefixed with a '$' character to ++ indicate that they're not normal fields (with the exception of ++ 'cpu', for compatibility with existing filter usage): ++ ++ $common_timestamp u64 - timestamp (from ring buffer) associated ++ with the event, in nanoseconds. May be ++ modified by .usecs to have timestamps ++ interpreted as microseconds. ++ cpu int - the cpu on which the event occurred. ++ + + 6.2 'hist' trigger examples + --------------------------- +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -224,6 +224,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 + return ts; + } + ++static u64 hist_field_cpu(struct hist_field *hist_field, ++ struct tracing_map_elt *elt, ++ struct ring_buffer_event *rbe, ++ void *event) ++{ ++ int cpu = raw_smp_processor_id(); ++ ++ return cpu; ++} ++ + 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 + field_name = hist_field_name(field->operands[0], ++level); + else if (field->flags & HIST_FIELD_FL_TIMESTAMP) + field_name = "$common_timestamp"; ++ else if (field->flags & HIST_FIELD_FL_CPU) ++ field_name = "cpu"; + 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 + goto out; + } + ++ if (flags & HIST_FIELD_FL_CPU) { ++ hist_field->fn = hist_field_cpu; ++ hist_field->size = sizeof(int); ++ hist_field->type = kstrdup("int", GFP_KERNEL); ++ if (!hist_field->type) ++ goto free; ++ goto out; ++ } ++ + if (WARN_ON_ONCE(!field)) + goto out; + +@@ -1980,7 +2002,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; +- } else { ++ } else if (strcmp(field_name, "cpu") == 0) ++ *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 + goto out; + } + } +- + 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 + ret = -EINVAL; + goto out; + } +- + 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_ + + if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP) + seq_puts(m, "$common_timestamp"); ++ else if (hist_field->flags & HIST_FIELD_FL_CPU) ++ seq_puts(m, "cpu"); + else if (field_name) + seq_printf(m, "%s", field_name); + diff --git a/patches/0028-s390-Prevent-hotplug-rwsem-recursion.patch b/patches/0028-s390-Prevent-hotplug-rwsem-recursion.patch index ec94de3bbaec8..1499ce1e48d45 100644 --- a/patches/0028-s390-Prevent-hotplug-rwsem-recursion.patch +++ b/patches/0028-s390-Prevent-hotplug-rwsem-recursion.patch @@ -1,4 +1,3 @@ -From 5d5dbc4ef27e72104dea6102e4d1a1bf5a8ed971 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:39 +0200 Subject: [PATCH 28/32] s390: Prevent hotplug rwsem recursion diff --git a/patches/0028-tracing-Add-hist-trigger-support-for-variable-refere.patch b/patches/0028-tracing-Add-hist-trigger-support-for-variable-refere.patch new file mode 100644 index 0000000000000..6993202a8e002 --- /dev/null +++ b/patches/0028-tracing-Add-hist-trigger-support-for-variable-refere.patch @@ -0,0 +1,105 @@ +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 + reference aliases + +Add support for alias=$somevar where alias can be used as +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(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -225,6 +225,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 + + if (field->field) + field_name = field->field->name; +- else if (field->flags & HIST_FIELD_FL_LOG2) ++ else if (field->flags & HIST_FIELD_FL_LOG2 || ++ field->flags & HIST_FIELD_FL_ALIAS) + 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 + + hist_field->hist_data = hist_data; + +- if (flags & HIST_FIELD_FL_EXPR) ++ if (flags & HIST_FIELD_FL_EXPR || flags & HIST_FIELD_FL_ALIAS) + goto out; /* caller will populate */ + + if (flags & HIST_FIELD_FL_VAR_REF) { +@@ -2013,6 +2015,34 @@ parse_field(struct hist_trigger_data *hi + return field; + } + ++static struct hist_field *create_alias(struct hist_trigger_data *hist_data, ++ struct hist_field *var_ref, ++ char *var_name) ++{ ++ struct hist_field *alias = NULL; ++ unsigned long flags = HIST_FIELD_FL_ALIAS | HIST_FIELD_FL_VAR | ++ HIST_FIELD_FL_VAR_ONLY; ++ ++ alias = create_hist_field(hist_data, NULL, flags, var_name); ++ if (!alias) ++ return NULL; ++ ++ 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); ++ destroy_hist_field(alias, 0); ++ return NULL; ++ } ++ ++ return alias; ++} ++ + 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; ++ } ++ } + return hist_field; + } + +@@ -4152,8 +4189,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"); +- else if (field_name) ++ else if (field_name) { ++ if (hist_field->flags & HIST_FIELD_FL_ALIAS) ++ seq_putc(m, '$'); + seq_printf(m, "%s", field_name); ++ } + + if (hist_field->flags) { + const char *flags_str = get_hist_field_flags(hist_field); diff --git a/patches/0029-cpu-hotplug-Convert-hotplug-locking-to-percpu-rwsem.patch b/patches/0029-cpu-hotplug-Convert-hotplug-locking-to-percpu-rwsem.patch index 25e340a31c1f5..79a107ed7b4c8 100644 --- a/patches/0029-cpu-hotplug-Convert-hotplug-locking-to-percpu-rwsem.patch +++ b/patches/0029-cpu-hotplug-Convert-hotplug-locking-to-percpu-rwsem.patch @@ -1,4 +1,3 @@ -From fc8dffd379ca5620664336eb895a426b42847558 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:40 +0200 Subject: [PATCH 29/32] cpu/hotplug: Convert hotplug locking to percpu rwsem diff --git a/patches/0029-tracing-Add-last-error-error-facility-for-hist-trigg.patch b/patches/0029-tracing-Add-last-error-error-facility-for-hist-trigg.patch new file mode 100644 index 0000000000000..325bfb8044217 --- /dev/null +++ b/patches/0029-tracing-Add-last-error-error-facility-for-hist-trigg.patch @@ -0,0 +1,499 @@ +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 + triggers + +With the addition of variables and actions, it's become necessary to +provide more detailed error information to users about syntax errors. + +Add a 'last error' facility accessible via the erroring event's 'hist' +file. Reading the hist file after an error will display more detailed +information about what went wrong, if information is available. This +extended error information will be available until the next hist +trigger command for that event. + + # echo xxx > /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger + echo: write error: Invalid argument + + # cat /sys/kernel/debug/tracing/events/sched/sched_wakeup/hist + + ERROR: Couldn't yyy: zzz + Last command: xxx + +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(-) + +--- a/Documentation/trace/events.txt ++++ b/Documentation/trace/events.txt +@@ -686,6 +686,25 @@ triggers (you have to use '!' for each o + interpreted as microseconds. + cpu int - the cpu on which the event occurred. + ++ Extended error information ++ -------------------------- ++ ++ For some error conditions encountered when invoking a hist trigger ++ command, extended error information is available via the ++ corresponding event's 'hist' file. Reading the hist file after an ++ error will display more detailed information about what went wrong, ++ if information is available. This extended error information will ++ be available until the next hist trigger command for that event. ++ ++ If available for a given error condition, the extended error ++ information and usage takes the following form: ++ ++ # echo xxx > /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger ++ echo: write error: Invalid argument ++ ++ # cat /sys/kernel/debug/tracing/events/sched/sched_wakeup/hist ++ ERROR: Couldn't yyy: zzz ++ Last command: xxx + + 6.2 'hist' trigger examples + --------------------------- +--- 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; + }; + ++ ++static char *hist_err_str; ++static char *last_hist_cmd; ++ ++static int hist_err_alloc(void) ++{ ++ int ret = 0; ++ ++ last_hist_cmd = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL); ++ hist_err_str = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL); ++ if (!last_hist_cmd || !hist_err_str) ++ ret = -ENOMEM; ++ ++ return ret; ++} ++ ++static void last_cmd_set(char *str) ++{ ++ if (!last_hist_cmd || !str) ++ return; ++ ++ if (strlen(last_hist_cmd) > MAX_FILTER_STR_VAL - 1) ++ return; ++ ++ strcpy(last_hist_cmd, str); ++} ++ ++static void hist_err(char *str, char *var) ++{ ++ int maxlen = MAX_FILTER_STR_VAL - 1; ++ ++ if (strlen(hist_err_str)) ++ return; ++ ++ if (!hist_err_str || !str) ++ return; ++ ++ if (!var) ++ var = ""; ++ ++ if (strlen(hist_err_str) + strlen(str) + strlen(var) > maxlen) ++ return; ++ ++ strcat(hist_err_str, str); ++ strcat(hist_err_str, var); ++} ++ ++static void hist_err_event(char *str, char *system, char *event, char *var) ++{ ++ char err[MAX_FILTER_STR_VAL]; ++ ++ if (system && var) ++ sprintf(err, "%s.%s.%s", system, event, var); ++ else if (system) ++ sprintf(err, "%s.%s", system, event); ++ else ++ strcpy(err, var); ++ ++ hist_err(str, err); ++} ++ ++static void hist_err_clear(void) ++{ ++ if (!hist_err_str) ++ return; ++ ++ hist_err_str[0] = '\0'; ++} ++ ++static bool have_hist_err(void) ++{ ++ if (hist_err_str && strlen(hist_err_str)) ++ return true; ++ ++ return false; ++} ++ + static LIST_HEAD(synth_event_list); + static DEFINE_MUTEX(synth_event_mutex); + +@@ -1954,12 +2032,21 @@ static struct hist_field *create_var_ref + return ref_field; + } + ++static bool is_common_field(char *var_name) ++{ ++ if (strncmp(var_name, "$common_timestamp", strlen("$common_timestamp")) == 0) ++ return true; ++ ++ return false; ++} ++ + static struct hist_field *parse_var_ref(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 (!var_name || strlen(var_name) < 2 || var_name[0] != '$' || ++ is_common_field(var_name)) + return NULL; + + var_name++; +@@ -1968,6 +2055,10 @@ static struct hist_field *parse_var_ref( + if (var_field) + ref_field = create_var_ref(var_field); + ++ if (!ref_field) ++ hist_err_event("Couldn't find variable: $", ++ system, event_name, var_name); ++ + return ref_field; + } + +@@ -2426,8 +2517,11 @@ create_field_var_hist(struct hist_trigge + char *cmd; + int ret; + +- if (target_hist_data->n_field_var_hists >= SYNTH_FIELDS_MAX) ++ if (target_hist_data->n_field_var_hists >= SYNTH_FIELDS_MAX) { ++ hist_err_event("onmatch: Too many field variables defined: ", ++ system, event_name, field_name); + return ERR_PTR(-EINVAL); ++ } + + tr = top_trace_array(); + if (!tr) +@@ -2435,13 +2529,18 @@ create_field_var_hist(struct hist_trigge + + file = event_file(system, event_name); + if (IS_ERR(file)) { ++ hist_err_event("onmatch: Event file not found: ", ++ system, event_name, field_name); + ret = PTR_ERR(file); + return ERR_PTR(ret); + } + + hist_data = find_compatible_hist(target_hist_data, file); +- if (!hist_data) ++ if (!hist_data) { ++ hist_err_event("onmatch: Matching event histogram not found: ", ++ system, event_name, field_name); + return ERR_PTR(-EINVAL); ++ } + + var_hist = kzalloc(sizeof(*var_hist), GFP_KERNEL); + if (!var_hist) +@@ -2489,6 +2588,8 @@ create_field_var_hist(struct hist_trigge + kfree(cmd); + kfree(var_hist->cmd); + kfree(var_hist); ++ hist_err_event("onmatch: Couldn't create histogram for field: ", ++ system, event_name, field_name); + return ERR_PTR(ret); + } + +@@ -2500,6 +2601,8 @@ create_field_var_hist(struct hist_trigge + kfree(cmd); + kfree(var_hist->cmd); + kfree(var_hist); ++ hist_err_event("onmatch: Couldn't find synthetic variable: ", ++ system, event_name, field_name); + return ERR_PTR(-EINVAL); + } + +@@ -2636,18 +2739,21 @@ static struct field_var *create_field_va + int ret = 0; + + if (hist_data->n_field_vars >= SYNTH_FIELDS_MAX) { ++ hist_err("Too many field variables defined: ", field_name); + ret = -EINVAL; + goto err; + } + + val = parse_atom(hist_data, file, field_name, &flags, NULL); + if (IS_ERR(val)) { ++ hist_err("Couldn't parse field variable: ", field_name); + ret = PTR_ERR(val); + goto err; + } + + var = create_var(hist_data, file, field_name, val->size, val->type); + if (IS_ERR(var)) { ++ hist_err("Couldn't create or find variable: ", field_name); + kfree(val); + ret = PTR_ERR(var); + goto err; +@@ -2772,14 +2878,18 @@ static int onmax_create(struct hist_trig + int ret = 0; + + 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); + return -EINVAL; ++ } + onmax_var_str++; + + event_name = trace_event_name(call); + var_field = find_target_event_var(hist_data, NULL, NULL, onmax_var_str); +- if (!var_field) ++ if (!var_field) { ++ hist_err("onmax: Couldn't find onmax variable: ", onmax_var_str); + return -EINVAL; ++ } + + 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; + 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 + + field_var = create_target_field_var(hist_data, NULL, NULL, param); + if (IS_ERR(field_var)) { ++ hist_err("onmax: Couldn't create field variable: ", param); + ret = PTR_ERR(field_var); + kfree(param); + goto out; +@@ -2847,6 +2959,7 @@ static int parse_action_params(char *par + + param = strstrip(param); + if (strlen(param) < 2) { ++ hist_err("Invalid action param: ", param); + ret = -EINVAL; + goto out; + } +@@ -3004,6 +3117,9 @@ onmatch_find_var(struct hist_trigger_dat + hist_field = find_event_var(system, event, var); + } + ++ if (!hist_field) ++ hist_err_event("onmatch: Couldn't find onmatch param: $", system, event, var); ++ + return hist_field; + } + +@@ -3055,6 +3171,7 @@ static int onmatch_create(struct hist_tr + + event = find_synth_event(data->synth_event_name); + if (!event) { ++ hist_err("onmatch: Couldn't find synthetic event: ", data->synth_event_name); + ret = -EINVAL; + goto out; + } +@@ -3094,6 +3211,7 @@ static int onmatch_create(struct hist_tr + ret = -EINVAL; + goto out; + } ++ + 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 + continue; + } + ++ hist_err_event("onmatch: Param type doesn't match synthetic event field type: ", ++ system, event_name, param); + kfree(p); + ret = -EINVAL; + goto out; + } + + if (field_pos != event->n_fields) { ++ hist_err("onmatch: Param count doesn't match synthetic event field count: ", event->name); + ret = -EINVAL; + goto out; + } +@@ -3141,31 +3262,44 @@ static struct action_data *onmatch_parse + return ERR_PTR(-ENOMEM); + + match_event = strsep(&str, ")"); +- if (!match_event || !str) ++ if (!match_event || !str) { ++ hist_err("onmatch: Missing closing paren: ", match_event); + goto free; ++ } + + match_event_system = strsep(&match_event, "."); +- if (!match_event) ++ if (!match_event) { ++ hist_err("onmatch: Missing subsystem for match event: ", match_event_system); + goto free; ++ } + +- if (IS_ERR(event_file(match_event_system, match_event))) ++ if (IS_ERR(event_file(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); + + strsep(&str, "."); +- if (!str) ++ if (!str) { ++ hist_err("onmatch: Missing . after onmatch(): ", str); + goto free; ++ } + + synth_event_name = strsep(&str, "("); +- if (!synth_event_name || !str) ++ if (!synth_event_name || !str) { ++ hist_err("onmatch: Missing opening paramlist paren: ", synth_event_name); + goto free; ++ } + data->synth_event_name = kstrdup(synth_event_name, GFP_KERNEL); + + params = strsep(&str, ")"); +- if (!params || !str || (str && strlen(str))) ++ if (!params || !str || (str && strlen(str))) { ++ hist_err("onmatch: Missing closing paramlist paren: ", params); + goto free; ++ } + + 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; + } +@@ -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, + hist_trigger_show(m, data, n++); + } + ++ if (have_hist_err()) { ++ seq_printf(m, "\nERROR: %s\n", hist_err_str); ++ seq_printf(m, " Last command: %s\n", last_hist_cmd); ++ } ++ + out_unlock: + mutex_unlock(&event_mutex); + +@@ -4509,6 +4652,7 @@ static int hist_register_trigger(char *g + if (named_data) { + if (!hist_trigger_match(data, named_data, named_data, + true)) { ++ hist_err("Named hist trigger doesn't match existing named trigger (includes variables): ", hist_data->attrs->name); + ret = -EINVAL; + goto out; + } +@@ -4528,13 +4672,16 @@ static int hist_register_trigger(char *g + test->paused = false; + else if (hist_data->attrs->clear) + hist_clear(test); +- else ++ else { ++ hist_err("Hist trigger already exists", NULL); + ret = -EEXIST; ++ } + goto out; + } + } + new: + if (hist_data->attrs->cont || hist_data->attrs->clear) { ++ hist_err("Can't clear or continue a nonexistent hist trigger", NULL); + ret = -ENOENT; + goto out; + } +@@ -4701,6 +4848,11 @@ static int event_hist_trigger_func(struc + char *trigger, *p; + int ret = 0; + ++ if (glob && strlen(glob)) { ++ last_cmd_set(param); ++ hist_err_clear(); ++ } ++ + if (!param) + return -EINVAL; + +@@ -4804,6 +4956,9 @@ static int event_hist_trigger_func(struc + /* Just return zero, not the number of registered triggers */ + ret = 0; + out: ++ if (ret == 0) ++ hist_err_clear(); ++ + 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 + goto err; + } + ++ hist_err_alloc(); ++ + return err; + err: + pr_warn("Could not create tracefs 'synthetic_events' entry\n"); diff --git a/patches/0030-sched-Provide-is_percpu_thread-helper.patch b/patches/0030-sched-Provide-is_percpu_thread-helper.patch index 18aec16e07730..92aa91a555fa1 100644 --- a/patches/0030-sched-Provide-is_percpu_thread-helper.patch +++ b/patches/0030-sched-Provide-is_percpu_thread-helper.patch @@ -1,4 +1,3 @@ -From 62ec05dd71b19f5be890a1992227cc7b2ac0adc4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:41 +0200 Subject: [PATCH 30/32] sched: Provide is_percpu_thread() helper diff --git a/patches/0030-tracing-Add-inter-event-hist-trigger-Documentation.patch b/patches/0030-tracing-Add-inter-event-hist-trigger-Documentation.patch new file mode 100644 index 0000000000000..18c4d2093b181 --- /dev/null +++ b/patches/0030-tracing-Add-inter-event-hist-trigger-Documentation.patch @@ -0,0 +1,402 @@ +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 + +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: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + Documentation/trace/events.txt | 376 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 376 insertions(+) + +--- a/Documentation/trace/events.txt ++++ b/Documentation/trace/events.txt +@@ -571,6 +571,7 @@ triggers (you have to use '!' for each o + .sym-offset display an address as a symbol and offset + .syscall display a syscall id as a system call name + .execname display a common_pid as a program name ++ .usecs display a $common_timestamp in microseconds + + 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 + Hits: 489 + Entries: 7 + Dropped: 0 ++ ++6.3 Inter-event hist triggers ++----------------------------- ++ ++Inter-event hist triggers are hist triggers that combine values from ++one or more other events and create a histogram using that data. Data ++from an inter-event histogram can in turn become the source for ++further combined histograms, thus providing a chain of related ++histograms, which is important for some applications. ++ ++The most important example of an inter-event quantity that can be used ++in this manner is latency, which is simply a difference in timestamps ++between two events (although trace events don't have an externally ++visible timestamp field, the inter-event hist trigger support adds a ++pseudo-field to all events named '$common_timestamp' which can be used ++as if it were an actual event field). Although latency is the most ++important inter-event quantity, note that because the support is ++completely general across the trace event subsystem, any event field ++can be used in an inter-event quantity. ++ ++An example of a histogram that combines data from other histograms ++into a useful chain would be a 'wakeupswitch latency' histogram that ++combines a 'wakeup latency' histogram and a 'switch latency' ++histogram. ++ ++Normally, a hist trigger specification consists of a (possibly ++compound) key along with one or more numeric values, which are ++continually updated sums associated with that key. A histogram ++specification in this case consists of individual key and value ++specifications that refer to trace event fields associated with a ++single event type. ++ ++The inter-event hist trigger extension allows fields from multiple ++events to be referenced and combined into a multi-event histogram ++specification. In support of this overall goal, a few enabling ++features have been added to the hist trigger support: ++ ++ - In order to compute an inter-event quantity, a value from one ++ event needs to saved and then referenced from another event. This ++ requires the introduction of support for histogram 'variables'. ++ ++ - The computation of inter-event quantities and their combination ++ require some minimal amount of support for applying simple ++ expressions to variables (+ and -). ++ ++ - A histogram consisting of inter-event quantities isn't logically a ++ histogram on either event (so having the 'hist' file for either ++ event host the histogram output doesn't really make sense). To ++ address the idea that the histogram is associated with a ++ combination of events, support is added allowing the creation of ++ 'synthetic' events that are events derived from other events. ++ These synthetic events are full-fledged events just like any other ++ and can be used as such, as for instance to create the ++ 'combination' histograms mentioned previously. ++ ++ - A set of 'actions' can be associated with histogram entries - ++ these can be used to generate the previously mentioned synthetic ++ events, but can also be used for other purposes, such as for ++ example saving context when a 'max' latency has been hit. ++ ++ - Trace events don't have a 'timestamp' associated with them, but ++ there is an implicit timestamp saved along with an event in the ++ underlying ftrace ring buffer. This timestamp is now exposed as a ++ a synthetic field named '$common_timestamp' which can be used in ++ histograms as if it were any other event field. Note that it has ++ a '$' prefixed to it - this is meant to indicate that it isn't an ++ actual field in the trace format but rather is a synthesized value ++ that nonetheless can be used as if it were an actual field. By ++ default it is in units of nanoseconds; appending '.usecs' to a ++ common_timestamp field changes the units to microseconds. ++ ++These features are decribed in more detail in the following sections. ++ ++6.3.1 Histogram Variables ++------------------------- ++ ++Variables are simply named locations used for saving and retrieving ++values between matching events. A 'matching' event is defined as an ++event that has a matching key - if a variable is saved for a histogram ++entry corresponding to that key, any subsequent event with a matching ++key can access that variable. ++ ++A variable's value is normally available to any subsequent event until ++it is set to something else by a subsequent event. The one exception ++to that rule is that any variable used in an expression is essentially ++'read-once' - once it's used by an expression in a subsequent event, ++it's reset to its 'unset' state, which means it can't be used again ++unless it's set again. This ensures not only that an event doesn't ++use an uninitialized variable in a calculation, but that that variable ++is used only once and not for any unrelated subsequent match. ++ ++The basic syntax for saving a variable is to simply prefix a unique ++variable name not corresponding to any keyword along with an '=' sign ++to any event field. ++ ++Either keys or values can be saved and retrieved in this way. This ++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 ++ ++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. ++ ++Because 'vals=' is used, the $common_timestamp variable value above ++will also be summed as a normal histogram value would (though for a ++timestamp it makes little sense). ++ ++The below shows that a key value can also be saved in the same way: ++ ++ # echo 'hist:key=timer_pid=common_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 ++as a value: ++ ++ # echo 'hist:keys=next_pid:ts1=$common_timestamp ... >> event/trigger ++ ++Multiple variables can be assigned at the same time. The below would ++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 ++ ++Any number of variables not bound to a 'vals=' prefix can also be ++assigned by simply separating them with colons. Below is the same ++thing but without the values being summed in the histogram: ++ ++ # echo 'hist:keys=pid:ts0=$common_timestamp:b=field1 ... >> event/trigger ++ ++Variables set as above can be referenced and used in expressions on ++another event. ++ ++For example, here's how a latency can be calculated: ++ ++ # echo 'hist:keys=pid,prio:ts0=$common_timestamp ... >> event1/trigger ++ # echo 'hist:keys=next_pid:wakeup_lat=$common_timestamp-$ts0 ... >> event2/trigger ++ ++In the first line above, the event's timetamp is saved into the ++variable ts0. In the next line, ts0 is subtracted from the second ++event's timestamp to produce the latency, which is then assigned into ++yet another variable, 'wakeup_lat'. The hist trigger below in turn ++makes use of the wakeup_lat variable to compute a combined latency ++using the same key and variable from yet another event: ++ ++ # echo 'hist:key=pid:wakeupswitch_lat=$wakeup_lat+$switchtime_lat ... >> event3/trigger ++ ++6.3.2 Synthetic Events ++---------------------- ++ ++Synthetic events are user-defined events generated from hist trigger ++variables or fields associated with one or more other events. Their ++purpose is to provide a mechanism for displaying data spanning ++multiple events consistent with the existing and already familiar ++usage for normal events. ++ ++To define a synthetic event, the user writes a simple specification ++consisting of the name of the new event along with one or more ++variables and their types, which can be any valid field type, ++separated by semicolons, to the tracing/synthetic_events file. ++ ++For instance, the following creates a new event named 'wakeup_latency' ++with 3 fields: lat, pid, and prio. Each of those fields is simply a ++variable reference to a variable on another event: ++ ++ # echo 'wakeup_latency \ ++ u64 lat; \ ++ pid_t pid; \ ++ int prio' >> \ ++ /sys/kernel/debug/tracing/synthetic_events ++ ++Reading the tracing/synthetic_events file lists all the currently ++defined synthetic events, in this case the event defined above: ++ ++ # cat /sys/kernel/debug/tracing/synthetic_events ++ wakeup_latency u64 lat; pid_t pid; int prio ++ ++An existing synthetic event definition can be removed by prepending ++the command that defined it with a '!': ++ ++ # echo '!wakeup_latency u64 lat pid_t pid int prio' >> \ ++ /sys/kernel/debug/tracing/synthetic_events ++ ++At this point, there isn't yet an actual 'wakeup_latency' event ++instantiated in the event subsytem - for this to happen, a 'hist ++trigger action' needs to be instantiated and bound to actual fields ++and variables defined on other events (see Section 6.3.3 below). ++ ++Once that is done, an event instance is created, and a histogram can ++be defined using it: ++ ++ # echo 'hist:keys=pid,prio,lat.log2:sort=pid,lat' >> \ ++ /sys/kernel/debug/tracing/events/synthetic/wakeup_latency/trigger ++ ++The new event is created under the tracing/events/synthetic/ directory ++and looks and behaves just like any other event: ++ ++ # ls /sys/kernel/debug/tracing/events/synthetic/wakeup_latency ++ enable filter format hist id trigger ++ ++Like any other event, once a histogram is enabled for the event, the ++output can be displayed by reading the event's 'hist' file. ++ ++6.3.3 Hist trigger 'actions' ++---------------------------- ++ ++A hist trigger 'action' is a function that's executed whenever a ++histogram entry is added or updated. ++ ++The default 'action' if no special function is explicity specified is ++as it always has been, to simply update the set of values associated ++with an entry. Some applications, however, may want to perform ++additional actions at that point, such as generate another event, or ++compare and save a maximum. ++ ++The following additional actions are available. To specify an action ++for a given event, simply specify the action between colons in the ++hist trigger specification. ++ ++ - onmatch(matching.event).<synthetic_event_name>(param list) ++ ++ The 'onmatch(matching.event).<synthetic_event_name>(params)' hist ++ trigger action is invoked whenever an event matches and the ++ histogram entry would be added or updated. It causes the named ++ synthetic event to be generated with the values given in the ++ 'param list'. The result is the generation of a synthetic event ++ that consists of the values contained in those variables at the ++ time the invoking event was hit. ++ ++ The 'param list' consists of one or more parameters which may be ++ either variables or fields defined on either the 'matching.event' ++ or the target event. The variables or fields specified in the ++ param list may be either fully-qualified or unqualified. If a ++ variable is specified as unqualified, it must be unique between ++ the two events. A field name used as a param can be unqualified ++ if it refers to the target event, but must be fully qualified if ++ it refers to the matching event. A fully-qualified name is of the ++ form 'system.event_name.$var_name' or 'system.event_name.field'. ++ ++ The 'matching.event' specification is simply the fully qualified ++ event name of the event that matches the target event for the ++ onmatch() functionality, in the form 'system.event_name'. ++ ++ Finally, the number and type of variables/fields in the 'param ++ list' must match the number and types of the fields in the ++ synthetic event being generated. ++ ++ As an example the below defines a simple synthetic event and uses ++ a variable defined on the sched_wakeup_new event as a parameter ++ when invoking the synthetic event. Here we define the synthetic ++ event: ++ ++ # echo 'wakeup_new_test pid_t pid' >> \ ++ /sys/kernel/debug/tracing/synthetic_events ++ ++ # cat /sys/kernel/debug/tracing/synthetic_events ++ wakeup_new_test pid_t pid ++ ++ The following hist trigger both defines the missing testpid ++ variable and specifies an onmatch() action that generates 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) 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:sort=pid' >> \ ++ /sys/kernel/debug/tracing/events/synthetic/wakeup_new_test/trigger ++ ++ Running 'cyclictest' should cause wakeup_new events to generate ++ wakeup_new_test synthetic events which should result in histogram ++ output in the wakeup_new_test event's hist file: ++ ++ # cat /sys/kernel/debug/tracing/events/synthetic/wakeup_new_test/hist ++ ++ A more typical usage would be to use two events to calculate a ++ latency. The following example uses a set of hist triggers to ++ produce a 'wakeup_latency' histogram: ++ ++ First, we define a 'wakeup_latency' synthetic event: ++ ++ # 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 ++ cyclictest thread, save the timestamp in a 'ts0' variable: ++ ++ # echo 'hist:keys=saved_pid=pid:ts0=$common_timestamp.usecs \ ++ if comm=="cyclictest"' >> \ ++ /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger ++ ++ Then, when the corresponding thread is actually scheduled onto the ++ CPU by a sched_switch event, calculate the latency and use that ++ along with another variable and an event field to generate a ++ wakeup_latency synthetic event: ++ ++ # echo 'hist:keys=next_pid:wakeup_lat=$common_timestamp.usecs-$ts0:\ ++ onmatch(sched.sched_wakeup).wakeup_latency($wakeup_lat,\ ++ $saved_pid,next_prio) if next_comm=="cyclictest"' >> \ ++ /sys/kernel/debug/tracing/events/sched/sched_switch/trigger ++ ++ We also need to create a histogram on the wakeup_latency synthetic ++ event in order to aggregate the generated synthetic event data: ++ ++ # echo 'hist:keys=pid,prio,lat:sort=pid,lat' >> \ ++ /sys/kernel/debug/tracing/events/synthetic/wakeup_latency/trigger ++ ++ Finally, once we've run cyclictest to actually generate some ++ events, we can see the output by looking at the wakeup_latency ++ synthetic event's hist file: ++ ++ # cat /sys/kernel/debug/tracing/events/synthetic/wakeup_latency/hist ++ ++ - onmax(var).save(field,...) ++ ++ The 'onmax(var).save(field,...)' hist trigger action is invoked ++ whenever the value of 'var' associated with a histogram entry ++ exceeds the current maximum contained in that variable. ++ ++ The end result is that the trace event fields specified as the ++ onmax.save() params will be saved if 'var' exceeds the current ++ maximum for that hist trigger entry. This allows context from the ++ event that exhibited the new maximum to be saved for later ++ reference. When the histogram is displayed, additional fields ++ 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 ++ 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 ++ maximum latency, the values specified in the save() fields are ++ recoreded: ++ ++ # 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:\ ++ 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 ++ ++ When the histogram is displayed, the max value and the saved ++ values 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 ++ ++ Totals: ++ Hits: 12970 ++ Entries: 2 ++ Dropped: 0 diff --git a/patches/0031-acpi-processor-Prevent-cpu-hotplug-deadlock.patch b/patches/0031-acpi-processor-Prevent-cpu-hotplug-deadlock.patch index d665d1bb11cfb..1919876084f20 100644 --- a/patches/0031-acpi-processor-Prevent-cpu-hotplug-deadlock.patch +++ b/patches/0031-acpi-processor-Prevent-cpu-hotplug-deadlock.patch @@ -1,4 +1,3 @@ -From 0266d81e9bf5cc1fe6405c0523dfa015fe55aae1 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:42 +0200 Subject: [PATCH 31/32] acpi/processor: Prevent cpu hotplug deadlock diff --git a/patches/0031-tracing-Make-tracing_set_clock-non-static.patch b/patches/0031-tracing-Make-tracing_set_clock-non-static.patch new file mode 100644 index 0000000000000..88e35cf67957a --- /dev/null +++ b/patches/0031-tracing-Make-tracing_set_clock-non-static.patch @@ -0,0 +1,39 @@ +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 + +Allow tracing code outside of trace.c to access tracing_set_clock(). + +Some applications may require a particular clock in order to function +properly, such as latency calculations. + +Also, add an accessor returning the current clock string. + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + kernel/trace/trace.c | 2 +- + kernel/trace/trace.h | 1 + + 2 files changed, 2 insertions(+), 1 deletion(-) + +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -5887,7 +5887,7 @@ static int tracing_clock_show(struct seq + return 0; + } + +-static int tracing_set_clock(struct trace_array *tr, const char *clockstr) ++int tracing_set_clock(struct trace_array *tr, const char *clockstr) + { + int i; + +--- a/kernel/trace/trace.h ++++ b/kernel/trace/trace.h +@@ -279,6 +279,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); ++extern int tracing_set_clock(struct trace_array *tr, const char *clockstr); + + extern bool trace_clock_in_ns(struct trace_array *tr); + diff --git a/patches/0032-cpuhotplug-Link-lock-stacks-for-hotplug-callbacks.patch b/patches/0032-cpuhotplug-Link-lock-stacks-for-hotplug-callbacks.patch index b86bdd88a22a0..1e907f77cd014 100644 --- a/patches/0032-cpuhotplug-Link-lock-stacks-for-hotplug-callbacks.patch +++ b/patches/0032-cpuhotplug-Link-lock-stacks-for-hotplug-callbacks.patch @@ -1,4 +1,3 @@ -From 49dfe2a6779717d9c18395684ee31bdc98b22e53 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 24 May 2017 10:15:43 +0200 Subject: [PATCH 32/32] cpuhotplug: Link lock stacks for hotplug callbacks diff --git a/patches/0032-tracing-Add-a-clock-attribute-for-hist-triggers.patch b/patches/0032-tracing-Add-a-clock-attribute-for-hist-triggers.patch new file mode 100644 index 0000000000000..71dd2da4d25fe --- /dev/null +++ b/patches/0032-tracing-Add-a-clock-attribute-for-hist-triggers.patch @@ -0,0 +1,115 @@ +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 + +The default clock if timestamps are used in a histogram is "global". +If timestamps aren't used, the clock is irrelevant. + +Use the "clock=" param only if you want to override the default +"global" clock for a histogram with timestamps. + +Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +--- + Documentation/trace/events.txt | 9 +++++++++ + kernel/trace/trace_events_hist.c | 34 +++++++++++++++++++++++++++++++--- + 2 files changed, 40 insertions(+), 3 deletions(-) + +--- a/Documentation/trace/events.txt ++++ b/Documentation/trace/events.txt +@@ -2173,6 +2173,15 @@ specification. In support of this overa + default it is in units of nanoseconds; appending '.usecs' to a + common_timestamp field changes the units to microseconds. + ++A note on inter-event timestamps: If $common_timestamp is used in a ++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 ++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. ++ + These features are decribed in more detail in the following sections. + + 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 { + char *vals_str; + char *sort_key_str; + char *name; ++ char *clock; + bool pause; + bool cont; + bool clear; +@@ -1586,6 +1587,7 @@ static void destroy_hist_trigger_attrs(s + kfree(attrs->sort_key_str); + kfree(attrs->keys_str); + kfree(attrs->vals_str); ++ kfree(attrs->clock); + kfree(attrs); + } + +@@ -1625,7 +1627,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); +- else if (strncmp(str, "size=", strlen("size=")) == 0) { ++ else if (strncmp(str, "clock=", strlen("clock=")) == 0) { ++ strsep(&str, "="); ++ if (!str) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ str = strstrip(str); ++ attrs->clock = kstrdup(str, GFP_KERNEL); ++ } else if (strncmp(str, "size=", strlen("size=")) == 0) { + int map_bits = parse_map_size(str); + + if (map_bits < 0) { +@@ -1688,6 +1699,12 @@ static struct hist_trigger_attrs *parse_ + goto free; + } + ++ if (!attrs->clock) { ++ attrs->clock = kstrdup("global", GFP_KERNEL); ++ if (!attrs->clock) ++ goto free; ++ } ++ + return attrs; + free: + destroy_hist_trigger_attrs(attrs); +@@ -4437,6 +4454,8 @@ static int event_hist_trigger_print(stru + seq_puts(m, ".descending"); + } + seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits)); ++ if (hist_data->enable_timestamps) ++ seq_printf(m, ":clock=%s", hist_data->attrs->clock); + + print_actions_spec(m, hist_data); + +@@ -4702,10 +4721,19 @@ static int hist_register_trigger(char *g + goto out; + } + +- ret++; ++ if (hist_data->enable_timestamps) { ++ char *clock = hist_data->attrs->clock; ++ ++ ret = tracing_set_clock(file->tr, hist_data->attrs->clock); ++ if (ret) { ++ hist_err("Couldn't set trace_clock: ", clock); ++ goto out; ++ } + +- if (hist_data->enable_timestamps) + tracing_set_time_stamp_abs(file->tr, true); ++ } ++ ++ ret++; + out: + return ret; + } diff --git a/patches/CPUFREQ-Loongson2-drop-set_cpus_allowed_ptr.patch b/patches/CPUFREQ-Loongson2-drop-set_cpus_allowed_ptr.patch index 943d62e2a39ab..06ba3563b23f8 100644 --- a/patches/CPUFREQ-Loongson2-drop-set_cpus_allowed_ptr.patch +++ b/patches/CPUFREQ-Loongson2-drop-set_cpus_allowed_ptr.patch @@ -1,4 +1,3 @@ -From 5ffb5cace8448c787c9f44e16a7b12f8c2866848 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Tue, 4 Apr 2017 17:43:55 +0200 Subject: [PATCH] CPUFREQ: Loongson2: drop set_cpus_allowed_ptr() diff --git a/patches/cond-resched-softirq-rt.patch b/patches/cond-resched-softirq-rt.patch index 407dbc8bff17b..53aa32afb1847 100644 --- a/patches/cond-resched-softirq-rt.patch +++ b/patches/cond-resched-softirq-rt.patch @@ -15,7 +15,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> --- a/include/linux/sched.h +++ b/include/linux/sched.h -@@ -1514,12 +1514,16 @@ extern int __cond_resched_lock(spinlock_ +@@ -1509,12 +1509,16 @@ extern int __cond_resched_lock(spinlock_ __cond_resched_lock(lock); \ }) diff --git a/patches/cpu-rt-rework-cpu-down.patch b/patches/cpu-rt-rework-cpu-down.patch index e68ab60434ac9..80e93f2b8f9af 100644 --- a/patches/cpu-rt-rework-cpu-down.patch +++ b/patches/cpu-rt-rework-cpu-down.patch @@ -56,7 +56,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> --- a/include/linux/sched.h +++ b/include/linux/sched.h -@@ -1347,6 +1347,10 @@ extern int task_can_attach(struct task_s +@@ -1342,6 +1342,10 @@ extern int task_can_attach(struct task_s #ifdef CONFIG_SMP extern void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask); extern int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask); @@ -67,7 +67,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> #else static inline void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask) { -@@ -1357,6 +1361,9 @@ static inline int set_cpus_allowed_ptr(s +@@ -1352,6 +1356,9 @@ static inline int set_cpus_allowed_ptr(s return -EINVAL; return 0; } diff --git a/patches/cpu_chill-Add-a-UNINTERRUPTIBLE-hrtimer_nanosleep.patch b/patches/cpu_chill-Add-a-UNINTERRUPTIBLE-hrtimer_nanosleep.patch index 162a2ef7ae6ca..6604d0695ae83 100644 --- a/patches/cpu_chill-Add-a-UNINTERRUPTIBLE-hrtimer_nanosleep.patch +++ b/patches/cpu_chill-Add-a-UNINTERRUPTIBLE-hrtimer_nanosleep.patch @@ -33,7 +33,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c -@@ -1622,12 +1622,13 @@ void hrtimer_init_sleeper(struct hrtimer +@@ -1601,12 +1601,13 @@ void hrtimer_init_sleeper(struct hrtimer } EXPORT_SYMBOL_GPL(hrtimer_init_sleeper); @@ -49,7 +49,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> hrtimer_start_expires(&t->timer, mode); if (likely(t->task)) -@@ -1669,7 +1670,8 @@ long __sched hrtimer_nanosleep_restart(s +@@ -1648,7 +1649,8 @@ long __sched hrtimer_nanosleep_restart(s HRTIMER_MODE_ABS); hrtimer_set_expires_tv64(&t.timer, restart->nanosleep.expires); @@ -59,7 +59,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> goto out; rmtp = restart->nanosleep.rmtp; -@@ -1686,8 +1688,10 @@ long __sched hrtimer_nanosleep_restart(s +@@ -1665,8 +1667,10 @@ long __sched hrtimer_nanosleep_restart(s return ret; } @@ -72,7 +72,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> { struct restart_block *restart; struct hrtimer_sleeper t; -@@ -1700,7 +1704,7 @@ long hrtimer_nanosleep(struct timespec * +@@ -1679,7 +1683,7 @@ long hrtimer_nanosleep(struct timespec * hrtimer_init_on_stack(&t.timer, clockid, mode); hrtimer_set_expires_range_ns(&t.timer, timespec_to_ktime(*rqtp), slack); @@ -81,7 +81,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> goto out; /* Absolute timers do not update the rmtp value and restart: */ -@@ -1727,6 +1731,12 @@ long hrtimer_nanosleep(struct timespec * +@@ -1706,6 +1710,12 @@ long hrtimer_nanosleep(struct timespec * return ret; } @@ -94,7 +94,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp, struct timespec __user *, rmtp) { -@@ -1753,7 +1763,8 @@ void cpu_chill(void) +@@ -1732,7 +1742,8 @@ void cpu_chill(void) unsigned int freeze_flag = current->flags & PF_NOFREEZE; current->flags |= PF_NOFREEZE; diff --git a/patches/delayacct-use-raw_spinlocks.patch b/patches/delayacct-use-raw_spinlocks.patch index 33018e7e1222b..5b5dcb84cc8f6 100644 --- a/patches/delayacct-use-raw_spinlocks.patch +++ b/patches/delayacct-use-raw_spinlocks.patch @@ -1,4 +1,3 @@ -From 2c887ccff27de53f76fbdedc0afea9fa3be3ea2f Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Sat, 20 May 2017 12:32:23 +0200 Subject: [PATCH] delayacct: use raw_spinlocks diff --git a/patches/ftrace-Fix-trace-header-alignment.patch b/patches/ftrace-Fix-trace-header-alignment.patch index 670b3d292a75e..ba263bd431827 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 -@@ -3113,17 +3113,17 @@ get_total_entries(struct trace_buffer *b +@@ -3121,17 +3121,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) -@@ -3152,11 +3152,11 @@ static void print_func_help_header_irq(s +@@ -3160,11 +3160,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 3236c2a374619..dd2ebfb8e4bc1 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 -@@ -1946,6 +1946,8 @@ tracing_generic_entry_update(struct trac +@@ -1954,6 +1954,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); -@@ -3114,9 +3116,10 @@ static void print_lat_help_header(struct +@@ -3122,9 +3124,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 3501f53fb95d2..551f4789d2e9d 100644 --- a/patches/futex-rtmutex-Cure-RT-double-blocking-issue.patch +++ b/patches/futex-rtmutex-Cure-RT-double-blocking-issue.patch @@ -1,4 +1,3 @@ -From 8a35f416ca9ff27e893cebcbe064a1f3c8e1de57 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Tue, 9 May 2017 17:11:10 +0200 Subject: [PATCH] futex/rtmutex: Cure RT double blocking issue diff --git a/patches/hrtimer-Move-schedule_work-call-to-helper-thread.patch b/patches/hrtimer-Move-schedule_work-call-to-helper-thread.patch index df0ebb63cb172..f9619243d11f1 100644 --- a/patches/hrtimer-Move-schedule_work-call-to-helper-thread.patch +++ b/patches/hrtimer-Move-schedule_work-call-to-helper-thread.patch @@ -51,7 +51,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c -@@ -696,6 +696,29 @@ static void hrtimer_switch_to_hres(void) +@@ -695,6 +695,29 @@ static void hrtimer_switch_to_hres(void) retrigger_next_event(NULL); } @@ -81,7 +81,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> static void clock_was_set_work(struct work_struct *work) { clock_was_set(); -@@ -711,6 +734,7 @@ void clock_was_set_delayed(void) +@@ -710,6 +733,7 @@ void clock_was_set_delayed(void) { schedule_work(&hrtimer_work); } diff --git a/patches/hrtimer-enfore-64byte-alignment.patch b/patches/hrtimer-enfore-64byte-alignment.patch index e6ad9f9136c1f..fccb336bdee66 100644 --- a/patches/hrtimer-enfore-64byte-alignment.patch +++ b/patches/hrtimer-enfore-64byte-alignment.patch @@ -13,7 +13,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h -@@ -116,11 +116,7 @@ struct hrtimer_sleeper { +@@ -112,11 +112,7 @@ struct hrtimer_sleeper { struct task_struct *task; }; diff --git a/patches/hrtimer-fixup-hrtimer-callback-changes-for-preempt-r.patch b/patches/hrtimer-fixup-hrtimer-callback-changes-for-preempt-r.patch index e3b4e25e1e5a0..f191a08096385 100644 --- a/patches/hrtimer-fixup-hrtimer-callback-changes-for-preempt-r.patch +++ b/patches/hrtimer-fixup-hrtimer-callback-changes-for-preempt-r.patch @@ -22,25 +22,25 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h -@@ -87,6 +87,8 @@ enum hrtimer_restart { +@@ -86,6 +86,8 @@ enum hrtimer_restart { + * was armed. * @function: timer expiry callback function * @base: pointer to the timer base (per cpu and per clock) - * @state: state information (See bit values above) + * @cb_entry: list entry to defer timers from hardirq context + * @irqsafe: timer can run in hardirq context - * @praecox: timer expiry time if expired at the time of programming + * @state: state information (See bit values above) * @is_rel: Set if the timer was armed relative * -@@ -98,6 +100,8 @@ struct hrtimer { +@@ -96,6 +98,8 @@ struct hrtimer { + ktime_t _softexpires; enum hrtimer_restart (*function)(struct hrtimer *); struct hrtimer_clock_base *base; - u8 state; + struct list_head cb_entry; + int irqsafe; - #ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST - ktime_t praecox; - #endif -@@ -125,6 +129,7 @@ struct hrtimer_sleeper { + u8 state; + u8 is_rel; + }; +@@ -121,6 +125,7 @@ struct hrtimer_sleeper { * timer to a base on another cpu. * @clockid: clock id for per_cpu support * @active: red black tree root node for the active timers @@ -48,7 +48,7 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> * @get_time: function to retrieve the current time of the clock * @offset: offset of this clock to the monotonic base */ -@@ -133,6 +138,7 @@ struct hrtimer_clock_base { +@@ -129,6 +134,7 @@ struct hrtimer_clock_base { int index; clockid_t clockid; struct timerqueue_head active; @@ -56,7 +56,7 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> ktime_t (*get_time)(void); ktime_t offset; } __attribute__((__aligned__(HRTIMER_CLOCK_BASE_ALIGN))); -@@ -176,6 +182,7 @@ struct hrtimer_cpu_base { +@@ -172,6 +178,7 @@ struct hrtimer_cpu_base { raw_spinlock_t lock; seqcount_t seq; struct hrtimer *running; @@ -86,7 +86,7 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c -@@ -720,11 +720,8 @@ static inline int hrtimer_is_hres_enable +@@ -719,11 +719,8 @@ static inline int hrtimer_is_hres_enable static inline void hrtimer_switch_to_hres(void) { } static inline void hrtimer_force_reprogram(struct hrtimer_cpu_base *base, int skip_equal) { } @@ -100,7 +100,7 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base) { } static inline void retrigger_next_event(void *arg) { } -@@ -845,7 +842,7 @@ void hrtimer_wait_for_timer(const struct +@@ -844,7 +841,7 @@ void hrtimer_wait_for_timer(const struct { struct hrtimer_clock_base *base = timer->base; @@ -109,7 +109,7 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> wait_event(base->cpu_base->wait, !(hrtimer_callback_running(timer))); } -@@ -895,6 +892,11 @@ static void __remove_hrtimer(struct hrti +@@ -894,6 +891,11 @@ static void __remove_hrtimer(struct hrti if (!(state & HRTIMER_STATE_ENQUEUED)) return; @@ -121,7 +121,7 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> if (!timerqueue_del(&base->active, &timer->node)) cpu_base->active_bases &= ~(1 << base->index); -@@ -1144,6 +1146,7 @@ static void __hrtimer_init(struct hrtime +@@ -1134,6 +1136,7 @@ static void __hrtimer_init(struct hrtime base = hrtimer_clockid_to_base(clock_id); timer->base = &cpu_base->clock_base[base]; @@ -129,7 +129,7 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> timerqueue_init(&timer->node); } -@@ -1178,6 +1181,7 @@ bool hrtimer_active(const struct hrtimer +@@ -1168,6 +1171,7 @@ bool hrtimer_active(const struct hrtimer seq = raw_read_seqcount_begin(&cpu_base->seq); if (timer->state != HRTIMER_STATE_INACTIVE || @@ -137,7 +137,7 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> cpu_base->running == timer) return true; -@@ -1275,12 +1279,111 @@ static void __run_hrtimer(struct hrtimer +@@ -1265,10 +1269,109 @@ static void __run_hrtimer(struct hrtimer cpu_base->running = NULL; } @@ -239,8 +239,6 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> + +#endif + - static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer); - static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now) { struct hrtimer_clock_base *base = cpu_base->clock_base; @@ -249,7 +247,7 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> for (; active; base++, active >>= 1) { struct timerqueue_node *node; -@@ -1320,9 +1423,14 @@ static void __hrtimer_run_queues(struct +@@ -1299,9 +1402,14 @@ static void __hrtimer_run_queues(struct if (basenow < hrtimer_get_softexpires_tv64(timer)) break; @@ -265,7 +263,7 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> } #ifdef CONFIG_HIGH_RES_TIMERS -@@ -1464,8 +1572,6 @@ void hrtimer_run_queues(void) +@@ -1443,8 +1551,6 @@ void hrtimer_run_queues(void) now = hrtimer_update_base(cpu_base); __hrtimer_run_queues(cpu_base, now); raw_spin_unlock(&cpu_base->lock); @@ -274,7 +272,7 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> } /* -@@ -1487,6 +1593,7 @@ static enum hrtimer_restart hrtimer_wake +@@ -1466,6 +1572,7 @@ static enum hrtimer_restart hrtimer_wake void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task) { sl->timer.function = hrtimer_wakeup; @@ -282,7 +280,7 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> sl->task = task; } EXPORT_SYMBOL_GPL(hrtimer_init_sleeper); -@@ -1621,6 +1728,7 @@ int hrtimers_prepare_cpu(unsigned int cp +@@ -1600,6 +1707,7 @@ int hrtimers_prepare_cpu(unsigned int cp for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) { cpu_base->clock_base[i].cpu_base = cpu_base; timerqueue_init_head(&cpu_base->clock_base[i].active); @@ -290,7 +288,7 @@ Signed-off-by: Ingo Molnar <mingo@elte.hu> } cpu_base->cpu = cpu; -@@ -1697,9 +1805,26 @@ int hrtimers_dead_cpu(unsigned int scpu) +@@ -1676,9 +1784,26 @@ int hrtimers_dead_cpu(unsigned int scpu) #endif /* CONFIG_HOTPLUG_CPU */ diff --git a/patches/hrtimers-prepare-full-preemption.patch b/patches/hrtimers-prepare-full-preemption.patch index 5475441f76c63..65cded2b0287c 100644 --- a/patches/hrtimers-prepare-full-preemption.patch +++ b/patches/hrtimers-prepare-full-preemption.patch @@ -25,7 +25,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> struct hrtimer_clock_base; struct hrtimer_cpu_base; -@@ -195,6 +196,9 @@ struct hrtimer_cpu_base { +@@ -191,6 +192,9 @@ struct hrtimer_cpu_base { unsigned int nr_hangs; unsigned int max_hang_time; #endif @@ -35,7 +35,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES]; } ____cacheline_aligned; -@@ -404,6 +408,13 @@ static inline void hrtimer_restart(struc +@@ -400,6 +404,13 @@ static inline void hrtimer_restart(struc hrtimer_start_expires(timer, HRTIMER_MODE_ABS); } @@ -49,7 +49,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> /* Query timers: */ extern ktime_t __hrtimer_get_remaining(const struct hrtimer *timer, bool adjust); -@@ -428,7 +439,7 @@ static inline int hrtimer_is_queued(stru +@@ -424,7 +435,7 @@ static inline int hrtimer_is_queued(stru * Helper function to check, whether the timer is running the callback * function */ @@ -60,7 +60,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> } --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c -@@ -828,6 +828,32 @@ u64 hrtimer_forward(struct hrtimer *time +@@ -827,6 +827,32 @@ u64 hrtimer_forward(struct hrtimer *time } EXPORT_SYMBOL_GPL(hrtimer_forward); @@ -93,7 +93,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> /* * enqueue_hrtimer - internal function to (re)start a timer * -@@ -1042,7 +1068,7 @@ int hrtimer_cancel(struct hrtimer *timer +@@ -1032,7 +1058,7 @@ int hrtimer_cancel(struct hrtimer *timer if (ret >= 0) return ret; @@ -102,7 +102,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> } } EXPORT_SYMBOL_GPL(hrtimer_cancel); -@@ -1438,6 +1464,8 @@ void hrtimer_run_queues(void) +@@ -1417,6 +1443,8 @@ void hrtimer_run_queues(void) now = hrtimer_update_base(cpu_base); __hrtimer_run_queues(cpu_base, now); raw_spin_unlock(&cpu_base->lock); @@ -111,7 +111,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> } /* -@@ -1597,6 +1625,9 @@ int hrtimers_prepare_cpu(unsigned int cp +@@ -1576,6 +1604,9 @@ int hrtimers_prepare_cpu(unsigned int cp cpu_base->cpu = cpu; hrtimer_init_hres(cpu_base); diff --git a/patches/kernel-sched-Provide-a-pointer-to-the-valid-CPU-mask.patch b/patches/kernel-sched-Provide-a-pointer-to-the-valid-CPU-mask.patch index 2549d7294409c..99c69f5ad3713 100644 --- a/patches/kernel-sched-Provide-a-pointer-to-the-valid-CPU-mask.patch +++ b/patches/kernel-sched-Provide-a-pointer-to-the-valid-CPU-mask.patch @@ -1,4 +1,3 @@ -From 866f2c8a7f0eec01a72cceeb73bab62eb3624694 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Tue, 4 Apr 2017 12:50:16 +0200 Subject: [PATCH] kernel: sched: Provide a pointer to the valid CPU mask diff --git a/patches/latency-hist.patch b/patches/latency-hist.patch deleted file mode 100644 index 59c19381fc0f2..0000000000000 --- a/patches/latency-hist.patch +++ /dev/null @@ -1,1818 +0,0 @@ -Subject: tracing: Add latency histograms -From: Carsten Emde <C.Emde@osadl.org> -Date: Tue, 19 Jul 2011 14:03:41 +0100 - -This patch provides a recording mechanism to store data of potential -sources of system latencies. The recordings separately determine the -latency caused by a delayed timer expiration, by a delayed wakeup of the -related user space program and by the sum of both. The histograms can be -enabled and reset individually. The data are accessible via the debug -filesystem. For details please consult Documentation/trace/histograms.txt. - -Signed-off-by: Carsten Emde <C.Emde@osadl.org> -Signed-off-by: Thomas Gleixner <tglx@linutronix.de> - ---- - Documentation/trace/histograms.txt | 186 +++++ - include/linux/hrtimer.h | 4 - include/linux/sched.h | 7 - include/trace/events/hist.h | 73 ++ - include/trace/events/latency_hist.h | 29 - kernel/time/hrtimer.c | 21 - kernel/trace/Kconfig | 104 +++ - kernel/trace/Makefile | 4 - kernel/trace/latency_hist.c | 1178 ++++++++++++++++++++++++++++++++++++ - kernel/trace/trace_irqsoff.c | 11 - 10 files changed, 1616 insertions(+), 1 deletion(-) - ---- /dev/null -+++ b/Documentation/trace/histograms.txt -@@ -0,0 +1,186 @@ -+ Using the Linux Kernel Latency Histograms -+ -+ -+This document gives a short explanation how to enable, configure and use -+latency histograms. Latency histograms are primarily relevant in the -+context of real-time enabled kernels (CONFIG_PREEMPT/CONFIG_PREEMPT_RT) -+and are used in the quality management of the Linux real-time -+capabilities. -+ -+ -+* Purpose of latency histograms -+ -+A latency histogram continuously accumulates the frequencies of latency -+data. There are two types of histograms -+- potential sources of latencies -+- effective latencies -+ -+ -+* Potential sources of latencies -+ -+Potential sources of latencies are code segments where interrupts, -+preemption or both are disabled (aka critical sections). To create -+histograms of potential sources of latency, the kernel stores the time -+stamp at the start of a critical section, determines the time elapsed -+when the end of the section is reached, and increments the frequency -+counter of that latency value - irrespective of whether any concurrently -+running process is affected by latency or not. -+- Configuration items (in the Kernel hacking/Tracers submenu) -+ CONFIG_INTERRUPT_OFF_LATENCY -+ CONFIG_PREEMPT_OFF_LATENCY -+ -+ -+* Effective latencies -+ -+Effective latencies are actually occuring during wakeup of a process. To -+determine effective latencies, the kernel stores the time stamp when a -+process is scheduled to be woken up, and determines the duration of the -+wakeup time shortly before control is passed over to this process. Note -+that the apparent latency in user space may be somewhat longer, since the -+process may be interrupted after control is passed over to it but before -+the execution in user space takes place. Simply measuring the interval -+between enqueuing and wakeup may also not appropriate in cases when a -+process is scheduled as a result of a timer expiration. The timer may have -+missed its deadline, e.g. due to disabled interrupts, but this latency -+would not be registered. Therefore, the offsets of missed timers are -+recorded in a separate histogram. If both wakeup latency and missed timer -+offsets are configured and enabled, a third histogram may be enabled that -+records the overall latency as a sum of the timer latency, if any, and the -+wakeup latency. This histogram is called "timerandwakeup". -+- Configuration items (in the Kernel hacking/Tracers submenu) -+ CONFIG_WAKEUP_LATENCY -+ CONFIG_MISSED_TIMER_OFSETS -+ -+ -+* Usage -+ -+The interface to the administration of the latency histograms is located -+in the debugfs file system. To mount it, either enter -+ -+mount -t sysfs nodev /sys -+mount -t debugfs nodev /sys/kernel/debug -+ -+from shell command line level, or add -+ -+nodev /sys sysfs defaults 0 0 -+nodev /sys/kernel/debug debugfs defaults 0 0 -+ -+to the file /etc/fstab. All latency histogram related files are then -+available in the directory /sys/kernel/debug/tracing/latency_hist. A -+particular histogram type is enabled by writing non-zero to the related -+variable in the /sys/kernel/debug/tracing/latency_hist/enable directory. -+Select "preemptirqsoff" for the histograms of potential sources of -+latencies and "wakeup" for histograms of effective latencies etc. The -+histogram data - one per CPU - are available in the files -+ -+/sys/kernel/debug/tracing/latency_hist/preemptoff/CPUx -+/sys/kernel/debug/tracing/latency_hist/irqsoff/CPUx -+/sys/kernel/debug/tracing/latency_hist/preemptirqsoff/CPUx -+/sys/kernel/debug/tracing/latency_hist/wakeup/CPUx -+/sys/kernel/debug/tracing/latency_hist/wakeup/sharedprio/CPUx -+/sys/kernel/debug/tracing/latency_hist/missed_timer_offsets/CPUx -+/sys/kernel/debug/tracing/latency_hist/timerandwakeup/CPUx -+ -+The histograms are reset by writing non-zero to the file "reset" in a -+particular latency directory. To reset all latency data, use -+ -+#!/bin/sh -+ -+TRACINGDIR=/sys/kernel/debug/tracing -+HISTDIR=$TRACINGDIR/latency_hist -+ -+if test -d $HISTDIR -+then -+ cd $HISTDIR -+ for i in `find . | grep /reset$` -+ do -+ echo 1 >$i -+ done -+fi -+ -+ -+* Data format -+ -+Latency data are stored with a resolution of one microsecond. The -+maximum latency is 10,240 microseconds. The data are only valid, if the -+overflow register is empty. Every output line contains the latency in -+microseconds in the first row and the number of samples in the second -+row. To display only lines with a positive latency count, use, for -+example, -+ -+grep -v " 0$" /sys/kernel/debug/tracing/latency_hist/preemptoff/CPU0 -+ -+#Minimum latency: 0 microseconds. -+#Average latency: 0 microseconds. -+#Maximum latency: 25 microseconds. -+#Total samples: 3104770694 -+#There are 0 samples greater or equal than 10240 microseconds -+#usecs samples -+ 0 2984486876 -+ 1 49843506 -+ 2 58219047 -+ 3 5348126 -+ 4 2187960 -+ 5 3388262 -+ 6 959289 -+ 7 208294 -+ 8 40420 -+ 9 4485 -+ 10 14918 -+ 11 18340 -+ 12 25052 -+ 13 19455 -+ 14 5602 -+ 15 969 -+ 16 47 -+ 17 18 -+ 18 14 -+ 19 1 -+ 20 3 -+ 21 2 -+ 22 5 -+ 23 2 -+ 25 1 -+ -+ -+* Wakeup latency of a selected process -+ -+To only collect wakeup latency data of a particular process, write the -+PID of the requested process to -+ -+/sys/kernel/debug/tracing/latency_hist/wakeup/pid -+ -+PIDs are not considered, if this variable is set to 0. -+ -+ -+* Details of the process with the highest wakeup latency so far -+ -+Selected data of the process that suffered from the highest wakeup -+latency that occurred in a particular CPU are available in the file -+ -+/sys/kernel/debug/tracing/latency_hist/wakeup/max_latency-CPUx. -+ -+In addition, other relevant system data at the time when the -+latency occurred are given. -+ -+The format of the data is (all in one line): -+<PID> <Priority> <Latency> (<Timeroffset>) <Command> \ -+<- <PID> <Priority> <Command> <Timestamp> -+ -+The value of <Timeroffset> is only relevant in the combined timer -+and wakeup latency recording. In the wakeup recording, it is -+always 0, in the missed_timer_offsets recording, it is the same -+as <Latency>. -+ -+When retrospectively searching for the origin of a latency and -+tracing was not enabled, it may be helpful to know the name and -+some basic data of the task that (finally) was switching to the -+late real-tlme task. In addition to the victim's data, also the -+data of the possible culprit are therefore displayed after the -+"<-" symbol. -+ -+Finally, the timestamp of the time when the latency occurred -+in <seconds>.<microseconds> after the most recent system boot -+is provided. -+ -+These data are also reset when the wakeup histogram is reset. ---- a/include/linux/hrtimer.h -+++ b/include/linux/hrtimer.h -@@ -86,6 +86,7 @@ enum hrtimer_restart { - * @function: timer expiry callback function - * @base: pointer to the timer base (per cpu and per clock) - * @state: state information (See bit values above) -+ * @praecox: timer expiry time if expired at the time of programming - * @is_rel: Set if the timer was armed relative - * - * The hrtimer structure must be initialized by hrtimer_init() -@@ -96,6 +97,9 @@ struct hrtimer { - enum hrtimer_restart (*function)(struct hrtimer *); - struct hrtimer_clock_base *base; - u8 state; -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ ktime_t praecox; -+#endif - u8 is_rel; - }; - ---- a/include/linux/sched.h -+++ b/include/linux/sched.h -@@ -1009,7 +1009,12 @@ struct task_struct { - /* Bitmask and counter of trace recursion: */ - unsigned long trace_recursion; - #endif /* CONFIG_TRACING */ -- -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ u64 preempt_timestamp_hist; -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ long timer_offset; -+#endif -+#endif - #ifdef CONFIG_KCOV - /* Coverage collection mode enabled for this task (0 if disabled): */ - enum kcov_mode kcov_mode; ---- /dev/null -+++ b/include/trace/events/hist.h -@@ -0,0 +1,73 @@ -+#undef TRACE_SYSTEM -+#define TRACE_SYSTEM hist -+ -+#if !defined(_TRACE_HIST_H) || defined(TRACE_HEADER_MULTI_READ) -+#define _TRACE_HIST_H -+ -+#include "latency_hist.h" -+#include <linux/tracepoint.h> -+ -+#if !defined(CONFIG_PREEMPT_OFF_HIST) && !defined(CONFIG_INTERRUPT_OFF_HIST) -+#define trace_preemptirqsoff_hist(a, b) -+#define trace_preemptirqsoff_hist_rcuidle(a, b) -+#else -+TRACE_EVENT(preemptirqsoff_hist, -+ -+ TP_PROTO(int reason, int starthist), -+ -+ TP_ARGS(reason, starthist), -+ -+ TP_STRUCT__entry( -+ __field(int, reason) -+ __field(int, starthist) -+ ), -+ -+ TP_fast_assign( -+ __entry->reason = reason; -+ __entry->starthist = starthist; -+ ), -+ -+ TP_printk("reason=%s starthist=%s", getaction(__entry->reason), -+ __entry->starthist ? "start" : "stop") -+); -+#endif -+ -+#ifndef CONFIG_MISSED_TIMER_OFFSETS_HIST -+#define trace_hrtimer_interrupt(a, b, c, d) -+#else -+TRACE_EVENT(hrtimer_interrupt, -+ -+ TP_PROTO(int cpu, long long offset, struct task_struct *curr, -+ struct task_struct *task), -+ -+ TP_ARGS(cpu, offset, curr, task), -+ -+ TP_STRUCT__entry( -+ __field(int, cpu) -+ __field(long long, offset) -+ __array(char, ccomm, TASK_COMM_LEN) -+ __field(int, cprio) -+ __array(char, tcomm, TASK_COMM_LEN) -+ __field(int, tprio) -+ ), -+ -+ TP_fast_assign( -+ __entry->cpu = cpu; -+ __entry->offset = offset; -+ memcpy(__entry->ccomm, curr->comm, TASK_COMM_LEN); -+ __entry->cprio = curr->prio; -+ memcpy(__entry->tcomm, task != NULL ? task->comm : "<none>", -+ task != NULL ? TASK_COMM_LEN : 7); -+ __entry->tprio = task != NULL ? task->prio : -1; -+ ), -+ -+ TP_printk("cpu=%d offset=%lld curr=%s[%d] thread=%s[%d]", -+ __entry->cpu, __entry->offset, __entry->ccomm, -+ __entry->cprio, __entry->tcomm, __entry->tprio) -+); -+#endif -+ -+#endif /* _TRACE_HIST_H */ -+ -+/* This part must be outside protection */ -+#include <trace/define_trace.h> ---- /dev/null -+++ b/include/trace/events/latency_hist.h -@@ -0,0 +1,29 @@ -+#ifndef _LATENCY_HIST_H -+#define _LATENCY_HIST_H -+ -+enum hist_action { -+ IRQS_ON, -+ PREEMPT_ON, -+ TRACE_STOP, -+ IRQS_OFF, -+ PREEMPT_OFF, -+ TRACE_START, -+}; -+ -+static char *actions[] = { -+ "IRQS_ON", -+ "PREEMPT_ON", -+ "TRACE_STOP", -+ "IRQS_OFF", -+ "PREEMPT_OFF", -+ "TRACE_START", -+}; -+ -+static inline char *getaction(int action) -+{ -+ if (action >= 0 && action <= sizeof(actions)/sizeof(actions[0])) -+ return actions[action]; -+ return "unknown"; -+} -+ -+#endif /* _LATENCY_HIST_H */ ---- a/kernel/time/hrtimer.c -+++ b/kernel/time/hrtimer.c -@@ -50,6 +50,7 @@ - #include <linux/sched/nohz.h> - #include <linux/sched/debug.h> - #include <linux/timer.h> -+#include <trace/events/hist.h> - #include <linux/freezer.h> - - #include <linux/uaccess.h> -@@ -960,7 +961,16 @@ void hrtimer_start_range_ns(struct hrtim - - /* Switch the timer base, if necessary: */ - new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED); -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ { -+ ktime_t now = new_base->get_time(); - -+ if (ktime_to_ns(tim) < ktime_to_ns(now)) -+ timer->praecox = now; -+ else -+ timer->praecox = ktime_set(0, 0); -+ } -+#endif - leftmost = enqueue_hrtimer(timer, new_base); - if (!leftmost) - goto unlock; -@@ -1239,6 +1249,8 @@ static void __run_hrtimer(struct hrtimer - cpu_base->running = NULL; - } - -+static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer); -+ - static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now) - { - struct hrtimer_clock_base *base = cpu_base->clock_base; -@@ -1258,6 +1270,15 @@ static void __hrtimer_run_queues(struct - - timer = container_of(node, struct hrtimer, node); - -+ trace_hrtimer_interrupt(raw_smp_processor_id(), -+ ktime_to_ns(ktime_sub(ktime_to_ns(timer->praecox) ? -+ timer->praecox : hrtimer_get_expires(timer), -+ basenow)), -+ current, -+ timer->function == hrtimer_wakeup ? -+ container_of(timer, struct hrtimer_sleeper, -+ timer)->task : NULL); -+ - /* - * The immediate goal for using the softexpires is - * minimizing wakeups, not running timers at the ---- a/kernel/trace/Kconfig -+++ b/kernel/trace/Kconfig -@@ -184,6 +184,24 @@ config IRQSOFF_TRACER - enabled. This option and the preempt-off timing option can be - used together or separately.) - -+config INTERRUPT_OFF_HIST -+ bool "Interrupts-off Latency Histogram" -+ depends on IRQSOFF_TRACER -+ help -+ This option generates continuously updated histograms (one per cpu) -+ of the duration of time periods with interrupts disabled. The -+ histograms are disabled by default. To enable them, write a non-zero -+ number to -+ -+ /sys/kernel/debug/tracing/latency_hist/enable/preemptirqsoff -+ -+ If PREEMPT_OFF_HIST is also selected, additional histograms (one -+ per cpu) are generated that accumulate the duration of time periods -+ when both interrupts and preemption are disabled. The histogram data -+ will be located in the debug file system at -+ -+ /sys/kernel/debug/tracing/latency_hist/irqsoff -+ - config PREEMPT_TRACER - bool "Preemption-off Latency Tracer" - default n -@@ -208,6 +226,24 @@ config PREEMPT_TRACER - enabled. This option and the irqs-off timing option can be - used together or separately.) - -+config PREEMPT_OFF_HIST -+ bool "Preemption-off Latency Histogram" -+ depends on PREEMPT_TRACER -+ help -+ This option generates continuously updated histograms (one per cpu) -+ of the duration of time periods with preemption disabled. The -+ histograms are disabled by default. To enable them, write a non-zero -+ number to -+ -+ /sys/kernel/debug/tracing/latency_hist/enable/preemptirqsoff -+ -+ If INTERRUPT_OFF_HIST is also selected, additional histograms (one -+ per cpu) are generated that accumulate the duration of time periods -+ when both interrupts and preemption are disabled. The histogram data -+ will be located in the debug file system at -+ -+ /sys/kernel/debug/tracing/latency_hist/preemptoff -+ - config SCHED_TRACER - bool "Scheduling Latency Tracer" - select GENERIC_TRACER -@@ -253,6 +289,74 @@ config HWLAT_TRACER - file. Every time a latency is greater than tracing_thresh, it will - be recorded into the ring buffer. - -+config WAKEUP_LATENCY_HIST -+ bool "Scheduling Latency Histogram" -+ depends on SCHED_TRACER -+ help -+ This option generates continuously updated histograms (one per cpu) -+ of the scheduling latency of the highest priority task. -+ The histograms are disabled by default. To enable them, write a -+ non-zero number to -+ -+ /sys/kernel/debug/tracing/latency_hist/enable/wakeup -+ -+ Two different algorithms are used, one to determine the latency of -+ processes that exclusively use the highest priority of the system and -+ another one to determine the latency of processes that share the -+ highest system priority with other processes. The former is used to -+ improve hardware and system software, the latter to optimize the -+ priority design of a given system. The histogram data will be -+ located in the debug file system at -+ -+ /sys/kernel/debug/tracing/latency_hist/wakeup -+ -+ and -+ -+ /sys/kernel/debug/tracing/latency_hist/wakeup/sharedprio -+ -+ If both Scheduling Latency Histogram and Missed Timer Offsets -+ Histogram are selected, additional histogram data will be collected -+ that contain, in addition to the wakeup latency, the timer latency, in -+ case the wakeup was triggered by an expired timer. These histograms -+ are available in the -+ -+ /sys/kernel/debug/tracing/latency_hist/timerandwakeup -+ -+ directory. They reflect the apparent interrupt and scheduling latency -+ and are best suitable to determine the worst-case latency of a given -+ system. To enable these histograms, write a non-zero number to -+ -+ /sys/kernel/debug/tracing/latency_hist/enable/timerandwakeup -+ -+config MISSED_TIMER_OFFSETS_HIST -+ depends on HIGH_RES_TIMERS -+ select GENERIC_TRACER -+ bool "Missed Timer Offsets Histogram" -+ help -+ Generate a histogram of missed timer offsets in microseconds. The -+ histograms are disabled by default. To enable them, write a non-zero -+ number to -+ -+ /sys/kernel/debug/tracing/latency_hist/enable/missed_timer_offsets -+ -+ The histogram data will be located in the debug file system at -+ -+ /sys/kernel/debug/tracing/latency_hist/missed_timer_offsets -+ -+ If both Scheduling Latency Histogram and Missed Timer Offsets -+ Histogram are selected, additional histogram data will be collected -+ that contain, in addition to the wakeup latency, the timer latency, in -+ case the wakeup was triggered by an expired timer. These histograms -+ are available in the -+ -+ /sys/kernel/debug/tracing/latency_hist/timerandwakeup -+ -+ directory. They reflect the apparent interrupt and scheduling latency -+ and are best suitable to determine the worst-case latency of a given -+ system. To enable these histograms, write a non-zero number to -+ -+ /sys/kernel/debug/tracing/latency_hist/enable/timerandwakeup -+ - config ENABLE_DEFAULT_TRACERS - bool "Trace process context switches and events" - depends on !GENERIC_TRACER ---- a/kernel/trace/Makefile -+++ b/kernel/trace/Makefile -@@ -38,6 +38,10 @@ obj-$(CONFIG_IRQSOFF_TRACER) += trace_ir - obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o - obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o - obj-$(CONFIG_HWLAT_TRACER) += trace_hwlat.o -+obj-$(CONFIG_INTERRUPT_OFF_HIST) += latency_hist.o -+obj-$(CONFIG_PREEMPT_OFF_HIST) += latency_hist.o -+obj-$(CONFIG_WAKEUP_LATENCY_HIST) += latency_hist.o -+obj-$(CONFIG_MISSED_TIMER_OFFSETS_HIST) += latency_hist.o - obj-$(CONFIG_NOP_TRACER) += trace_nop.o - obj-$(CONFIG_STACK_TRACER) += trace_stack.o - obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o ---- /dev/null -+++ b/kernel/trace/latency_hist.c -@@ -0,0 +1,1178 @@ -+/* -+ * kernel/trace/latency_hist.c -+ * -+ * Add support for histograms of preemption-off latency and -+ * interrupt-off latency and wakeup latency, it depends on -+ * Real-Time Preemption Support. -+ * -+ * Copyright (C) 2005 MontaVista Software, Inc. -+ * Yi Yang <yyang@ch.mvista.com> -+ * -+ * Converted to work with the new latency tracer. -+ * Copyright (C) 2008 Red Hat, Inc. -+ * Steven Rostedt <srostedt@redhat.com> -+ * -+ */ -+#include <linux/module.h> -+#include <linux/debugfs.h> -+#include <linux/seq_file.h> -+#include <linux/percpu.h> -+#include <linux/kallsyms.h> -+#include <linux/uaccess.h> -+#include <linux/sched.h> -+#include <linux/sched/rt.h> -+#include <linux/slab.h> -+#include <linux/atomic.h> -+#include <asm/div64.h> -+ -+#include "trace.h" -+#include <trace/events/sched.h> -+ -+#define NSECS_PER_USECS 1000L -+ -+#define CREATE_TRACE_POINTS -+#include <trace/events/hist.h> -+ -+enum { -+ IRQSOFF_LATENCY = 0, -+ PREEMPTOFF_LATENCY, -+ PREEMPTIRQSOFF_LATENCY, -+ WAKEUP_LATENCY, -+ WAKEUP_LATENCY_SHAREDPRIO, -+ MISSED_TIMER_OFFSETS, -+ TIMERANDWAKEUP_LATENCY, -+ MAX_LATENCY_TYPE, -+}; -+ -+#define MAX_ENTRY_NUM 10240 -+ -+struct hist_data { -+ atomic_t hist_mode; /* 0 log, 1 don't log */ -+ long offset; /* set it to MAX_ENTRY_NUM/2 for a bipolar scale */ -+ long min_lat; -+ long max_lat; -+ unsigned long long below_hist_bound_samples; -+ unsigned long long above_hist_bound_samples; -+ long long accumulate_lat; -+ unsigned long long total_samples; -+ unsigned long long hist_array[MAX_ENTRY_NUM]; -+}; -+ -+struct enable_data { -+ int latency_type; -+ int enabled; -+}; -+ -+static char *latency_hist_dir_root = "latency_hist"; -+ -+#ifdef CONFIG_INTERRUPT_OFF_HIST -+static DEFINE_PER_CPU(struct hist_data, irqsoff_hist); -+static char *irqsoff_hist_dir = "irqsoff"; -+static DEFINE_PER_CPU(cycles_t, hist_irqsoff_start); -+static DEFINE_PER_CPU(int, hist_irqsoff_counting); -+#endif -+ -+#ifdef CONFIG_PREEMPT_OFF_HIST -+static DEFINE_PER_CPU(struct hist_data, preemptoff_hist); -+static char *preemptoff_hist_dir = "preemptoff"; -+static DEFINE_PER_CPU(cycles_t, hist_preemptoff_start); -+static DEFINE_PER_CPU(int, hist_preemptoff_counting); -+#endif -+ -+#if defined(CONFIG_PREEMPT_OFF_HIST) && defined(CONFIG_INTERRUPT_OFF_HIST) -+static DEFINE_PER_CPU(struct hist_data, preemptirqsoff_hist); -+static char *preemptirqsoff_hist_dir = "preemptirqsoff"; -+static DEFINE_PER_CPU(cycles_t, hist_preemptirqsoff_start); -+static DEFINE_PER_CPU(int, hist_preemptirqsoff_counting); -+#endif -+ -+#if defined(CONFIG_PREEMPT_OFF_HIST) || defined(CONFIG_INTERRUPT_OFF_HIST) -+static notrace void probe_preemptirqsoff_hist(void *v, int reason, int start); -+static struct enable_data preemptirqsoff_enabled_data = { -+ .latency_type = PREEMPTIRQSOFF_LATENCY, -+ .enabled = 0, -+}; -+#endif -+ -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+struct maxlatproc_data { -+ char comm[FIELD_SIZEOF(struct task_struct, comm)]; -+ char current_comm[FIELD_SIZEOF(struct task_struct, comm)]; -+ int pid; -+ int current_pid; -+ int prio; -+ int current_prio; -+ long latency; -+ long timeroffset; -+ u64 timestamp; -+}; -+#endif -+ -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+static DEFINE_PER_CPU(struct hist_data, wakeup_latency_hist); -+static DEFINE_PER_CPU(struct hist_data, wakeup_latency_hist_sharedprio); -+static char *wakeup_latency_hist_dir = "wakeup"; -+static char *wakeup_latency_hist_dir_sharedprio = "sharedprio"; -+static notrace void probe_wakeup_latency_hist_start(void *v, -+ struct task_struct *p, int success); -+static notrace void probe_wakeup_latency_hist_stop(void *v, -+ struct task_struct *prev, struct task_struct *next); -+static notrace void probe_sched_migrate_task(void *, -+ struct task_struct *task, int cpu); -+static struct enable_data wakeup_latency_enabled_data = { -+ .latency_type = WAKEUP_LATENCY, -+ .enabled = 0, -+}; -+static DEFINE_PER_CPU(struct maxlatproc_data, wakeup_maxlatproc); -+static DEFINE_PER_CPU(struct maxlatproc_data, wakeup_maxlatproc_sharedprio); -+static DEFINE_PER_CPU(struct task_struct *, wakeup_task); -+static DEFINE_PER_CPU(int, wakeup_sharedprio); -+static unsigned long wakeup_pid; -+#endif -+ -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+static DEFINE_PER_CPU(struct hist_data, missed_timer_offsets); -+static char *missed_timer_offsets_dir = "missed_timer_offsets"; -+static notrace void probe_hrtimer_interrupt(void *v, int cpu, -+ long long offset, struct task_struct *curr, struct task_struct *task); -+static struct enable_data missed_timer_offsets_enabled_data = { -+ .latency_type = MISSED_TIMER_OFFSETS, -+ .enabled = 0, -+}; -+static DEFINE_PER_CPU(struct maxlatproc_data, missed_timer_offsets_maxlatproc); -+static unsigned long missed_timer_offsets_pid; -+#endif -+ -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+static DEFINE_PER_CPU(struct hist_data, timerandwakeup_latency_hist); -+static char *timerandwakeup_latency_hist_dir = "timerandwakeup"; -+static struct enable_data timerandwakeup_enabled_data = { -+ .latency_type = TIMERANDWAKEUP_LATENCY, -+ .enabled = 0, -+}; -+static DEFINE_PER_CPU(struct maxlatproc_data, timerandwakeup_maxlatproc); -+#endif -+ -+void notrace latency_hist(int latency_type, int cpu, long latency, -+ long timeroffset, u64 stop, -+ struct task_struct *p) -+{ -+ struct hist_data *my_hist; -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ struct maxlatproc_data *mp = NULL; -+#endif -+ -+ if (!cpu_possible(cpu) || latency_type < 0 || -+ latency_type >= MAX_LATENCY_TYPE) -+ return; -+ -+ switch (latency_type) { -+#ifdef CONFIG_INTERRUPT_OFF_HIST -+ case IRQSOFF_LATENCY: -+ my_hist = &per_cpu(irqsoff_hist, cpu); -+ break; -+#endif -+#ifdef CONFIG_PREEMPT_OFF_HIST -+ case PREEMPTOFF_LATENCY: -+ my_hist = &per_cpu(preemptoff_hist, cpu); -+ break; -+#endif -+#if defined(CONFIG_PREEMPT_OFF_HIST) && defined(CONFIG_INTERRUPT_OFF_HIST) -+ case PREEMPTIRQSOFF_LATENCY: -+ my_hist = &per_cpu(preemptirqsoff_hist, cpu); -+ break; -+#endif -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ case WAKEUP_LATENCY: -+ my_hist = &per_cpu(wakeup_latency_hist, cpu); -+ mp = &per_cpu(wakeup_maxlatproc, cpu); -+ break; -+ case WAKEUP_LATENCY_SHAREDPRIO: -+ my_hist = &per_cpu(wakeup_latency_hist_sharedprio, cpu); -+ mp = &per_cpu(wakeup_maxlatproc_sharedprio, cpu); -+ break; -+#endif -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ case MISSED_TIMER_OFFSETS: -+ my_hist = &per_cpu(missed_timer_offsets, cpu); -+ mp = &per_cpu(missed_timer_offsets_maxlatproc, cpu); -+ break; -+#endif -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ case TIMERANDWAKEUP_LATENCY: -+ my_hist = &per_cpu(timerandwakeup_latency_hist, cpu); -+ mp = &per_cpu(timerandwakeup_maxlatproc, cpu); -+ break; -+#endif -+ -+ default: -+ return; -+ } -+ -+ latency += my_hist->offset; -+ -+ if (atomic_read(&my_hist->hist_mode) == 0) -+ return; -+ -+ if (latency < 0 || latency >= MAX_ENTRY_NUM) { -+ if (latency < 0) -+ my_hist->below_hist_bound_samples++; -+ else -+ my_hist->above_hist_bound_samples++; -+ } else -+ my_hist->hist_array[latency]++; -+ -+ if (unlikely(latency > my_hist->max_lat || -+ my_hist->min_lat == LONG_MAX)) { -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ if (latency_type == WAKEUP_LATENCY || -+ latency_type == WAKEUP_LATENCY_SHAREDPRIO || -+ latency_type == MISSED_TIMER_OFFSETS || -+ latency_type == TIMERANDWAKEUP_LATENCY) { -+ strncpy(mp->comm, p->comm, sizeof(mp->comm)); -+ strncpy(mp->current_comm, current->comm, -+ sizeof(mp->current_comm)); -+ mp->pid = task_pid_nr(p); -+ mp->current_pid = task_pid_nr(current); -+ mp->prio = p->prio; -+ mp->current_prio = current->prio; -+ mp->latency = latency; -+ mp->timeroffset = timeroffset; -+ mp->timestamp = stop; -+ } -+#endif -+ my_hist->max_lat = latency; -+ } -+ if (unlikely(latency < my_hist->min_lat)) -+ my_hist->min_lat = latency; -+ my_hist->total_samples++; -+ my_hist->accumulate_lat += latency; -+} -+ -+static void *l_start(struct seq_file *m, loff_t *pos) -+{ -+ loff_t *index_ptr = NULL; -+ loff_t index = *pos; -+ struct hist_data *my_hist = m->private; -+ -+ if (index == 0) { -+ char minstr[32], avgstr[32], maxstr[32]; -+ -+ atomic_dec(&my_hist->hist_mode); -+ -+ if (likely(my_hist->total_samples)) { -+ long avg = (long) div64_s64(my_hist->accumulate_lat, -+ my_hist->total_samples); -+ snprintf(minstr, sizeof(minstr), "%ld", -+ my_hist->min_lat - my_hist->offset); -+ snprintf(avgstr, sizeof(avgstr), "%ld", -+ avg - my_hist->offset); -+ snprintf(maxstr, sizeof(maxstr), "%ld", -+ my_hist->max_lat - my_hist->offset); -+ } else { -+ strcpy(minstr, "<undef>"); -+ strcpy(avgstr, minstr); -+ strcpy(maxstr, minstr); -+ } -+ -+ seq_printf(m, "#Minimum latency: %s microseconds\n" -+ "#Average latency: %s microseconds\n" -+ "#Maximum latency: %s microseconds\n" -+ "#Total samples: %llu\n" -+ "#There are %llu samples lower than %ld" -+ " microseconds.\n" -+ "#There are %llu samples greater or equal" -+ " than %ld microseconds.\n" -+ "#usecs\t%16s\n", -+ minstr, avgstr, maxstr, -+ my_hist->total_samples, -+ my_hist->below_hist_bound_samples, -+ -my_hist->offset, -+ my_hist->above_hist_bound_samples, -+ MAX_ENTRY_NUM - my_hist->offset, -+ "samples"); -+ } -+ if (index < MAX_ENTRY_NUM) { -+ index_ptr = kmalloc(sizeof(loff_t), GFP_KERNEL); -+ if (index_ptr) -+ *index_ptr = index; -+ } -+ -+ return index_ptr; -+} -+ -+static void *l_next(struct seq_file *m, void *p, loff_t *pos) -+{ -+ loff_t *index_ptr = p; -+ struct hist_data *my_hist = m->private; -+ -+ if (++*pos >= MAX_ENTRY_NUM) { -+ atomic_inc(&my_hist->hist_mode); -+ return NULL; -+ } -+ *index_ptr = *pos; -+ return index_ptr; -+} -+ -+static void l_stop(struct seq_file *m, void *p) -+{ -+ kfree(p); -+} -+ -+static int l_show(struct seq_file *m, void *p) -+{ -+ int index = *(loff_t *) p; -+ struct hist_data *my_hist = m->private; -+ -+ seq_printf(m, "%6ld\t%16llu\n", index - my_hist->offset, -+ my_hist->hist_array[index]); -+ return 0; -+} -+ -+static const struct seq_operations latency_hist_seq_op = { -+ .start = l_start, -+ .next = l_next, -+ .stop = l_stop, -+ .show = l_show -+}; -+ -+static int latency_hist_open(struct inode *inode, struct file *file) -+{ -+ int ret; -+ -+ ret = seq_open(file, &latency_hist_seq_op); -+ if (!ret) { -+ struct seq_file *seq = file->private_data; -+ seq->private = inode->i_private; -+ } -+ return ret; -+} -+ -+static const struct file_operations latency_hist_fops = { -+ .open = latency_hist_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = seq_release, -+}; -+ -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+static void clear_maxlatprocdata(struct maxlatproc_data *mp) -+{ -+ mp->comm[0] = mp->current_comm[0] = '\0'; -+ mp->prio = mp->current_prio = mp->pid = mp->current_pid = -+ mp->latency = mp->timeroffset = -1; -+ mp->timestamp = 0; -+} -+#endif -+ -+static void hist_reset(struct hist_data *hist) -+{ -+ atomic_dec(&hist->hist_mode); -+ -+ memset(hist->hist_array, 0, sizeof(hist->hist_array)); -+ hist->below_hist_bound_samples = 0ULL; -+ hist->above_hist_bound_samples = 0ULL; -+ hist->min_lat = LONG_MAX; -+ hist->max_lat = LONG_MIN; -+ hist->total_samples = 0ULL; -+ hist->accumulate_lat = 0LL; -+ -+ atomic_inc(&hist->hist_mode); -+} -+ -+static ssize_t -+latency_hist_reset(struct file *file, const char __user *a, -+ size_t size, loff_t *off) -+{ -+ int cpu; -+ struct hist_data *hist = NULL; -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ struct maxlatproc_data *mp = NULL; -+#endif -+ off_t latency_type = (off_t) file->private_data; -+ -+ for_each_online_cpu(cpu) { -+ -+ switch (latency_type) { -+#ifdef CONFIG_PREEMPT_OFF_HIST -+ case PREEMPTOFF_LATENCY: -+ hist = &per_cpu(preemptoff_hist, cpu); -+ break; -+#endif -+#ifdef CONFIG_INTERRUPT_OFF_HIST -+ case IRQSOFF_LATENCY: -+ hist = &per_cpu(irqsoff_hist, cpu); -+ break; -+#endif -+#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) -+ case PREEMPTIRQSOFF_LATENCY: -+ hist = &per_cpu(preemptirqsoff_hist, cpu); -+ break; -+#endif -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ case WAKEUP_LATENCY: -+ hist = &per_cpu(wakeup_latency_hist, cpu); -+ mp = &per_cpu(wakeup_maxlatproc, cpu); -+ break; -+ case WAKEUP_LATENCY_SHAREDPRIO: -+ hist = &per_cpu(wakeup_latency_hist_sharedprio, cpu); -+ mp = &per_cpu(wakeup_maxlatproc_sharedprio, cpu); -+ break; -+#endif -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ case MISSED_TIMER_OFFSETS: -+ hist = &per_cpu(missed_timer_offsets, cpu); -+ mp = &per_cpu(missed_timer_offsets_maxlatproc, cpu); -+ break; -+#endif -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ case TIMERANDWAKEUP_LATENCY: -+ hist = &per_cpu(timerandwakeup_latency_hist, cpu); -+ mp = &per_cpu(timerandwakeup_maxlatproc, cpu); -+ break; -+#endif -+ } -+ -+ hist_reset(hist); -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ if (latency_type == WAKEUP_LATENCY || -+ latency_type == WAKEUP_LATENCY_SHAREDPRIO || -+ latency_type == MISSED_TIMER_OFFSETS || -+ latency_type == TIMERANDWAKEUP_LATENCY) -+ clear_maxlatprocdata(mp); -+#endif -+ } -+ -+ return size; -+} -+ -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+static ssize_t -+show_pid(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) -+{ -+ char buf[64]; -+ int r; -+ unsigned long *this_pid = file->private_data; -+ -+ r = snprintf(buf, sizeof(buf), "%lu\n", *this_pid); -+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); -+} -+ -+static ssize_t do_pid(struct file *file, const char __user *ubuf, -+ size_t cnt, loff_t *ppos) -+{ -+ char buf[64]; -+ unsigned long pid; -+ unsigned long *this_pid = file->private_data; -+ -+ if (cnt >= sizeof(buf)) -+ return -EINVAL; -+ -+ if (copy_from_user(&buf, ubuf, cnt)) -+ return -EFAULT; -+ -+ buf[cnt] = '\0'; -+ -+ if (kstrtoul(buf, 10, &pid)) -+ return -EINVAL; -+ -+ *this_pid = pid; -+ -+ return cnt; -+} -+#endif -+ -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+static ssize_t -+show_maxlatproc(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) -+{ -+ int r; -+ struct maxlatproc_data *mp = file->private_data; -+ int strmaxlen = (TASK_COMM_LEN * 2) + (8 * 8); -+ unsigned long long t; -+ unsigned long usecs, secs; -+ char *buf; -+ -+ if (mp->pid == -1 || mp->current_pid == -1) { -+ buf = "(none)\n"; -+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, -+ strlen(buf)); -+ } -+ -+ buf = kmalloc(strmaxlen, GFP_KERNEL); -+ if (buf == NULL) -+ return -ENOMEM; -+ -+ t = ns2usecs(mp->timestamp); -+ usecs = do_div(t, USEC_PER_SEC); -+ secs = (unsigned long) t; -+ r = snprintf(buf, strmaxlen, -+ "%d %d %ld (%ld) %s <- %d %d %s %lu.%06lu\n", mp->pid, -+ MAX_RT_PRIO-1 - mp->prio, mp->latency, mp->timeroffset, mp->comm, -+ mp->current_pid, MAX_RT_PRIO-1 - mp->current_prio, mp->current_comm, -+ secs, usecs); -+ r = simple_read_from_buffer(ubuf, cnt, ppos, buf, r); -+ kfree(buf); -+ return r; -+} -+#endif -+ -+static ssize_t -+show_enable(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) -+{ -+ char buf[64]; -+ struct enable_data *ed = file->private_data; -+ int r; -+ -+ r = snprintf(buf, sizeof(buf), "%d\n", ed->enabled); -+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); -+} -+ -+static ssize_t -+do_enable(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) -+{ -+ char buf[64]; -+ long enable; -+ struct enable_data *ed = file->private_data; -+ -+ if (cnt >= sizeof(buf)) -+ return -EINVAL; -+ -+ if (copy_from_user(&buf, ubuf, cnt)) -+ return -EFAULT; -+ -+ buf[cnt] = 0; -+ -+ if (kstrtoul(buf, 10, &enable)) -+ return -EINVAL; -+ -+ if ((enable && ed->enabled) || (!enable && !ed->enabled)) -+ return cnt; -+ -+ if (enable) { -+ int ret; -+ -+ switch (ed->latency_type) { -+#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST) -+ case PREEMPTIRQSOFF_LATENCY: -+ ret = register_trace_preemptirqsoff_hist( -+ probe_preemptirqsoff_hist, NULL); -+ if (ret) { -+ pr_info("wakeup trace: Couldn't assign " -+ "probe_preemptirqsoff_hist " -+ "to trace_preemptirqsoff_hist\n"); -+ return ret; -+ } -+ break; -+#endif -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ case WAKEUP_LATENCY: -+ ret = register_trace_sched_wakeup( -+ probe_wakeup_latency_hist_start, NULL); -+ if (ret) { -+ pr_info("wakeup trace: Couldn't assign " -+ "probe_wakeup_latency_hist_start " -+ "to trace_sched_wakeup\n"); -+ return ret; -+ } -+ ret = register_trace_sched_wakeup_new( -+ probe_wakeup_latency_hist_start, NULL); -+ if (ret) { -+ pr_info("wakeup trace: Couldn't assign " -+ "probe_wakeup_latency_hist_start " -+ "to trace_sched_wakeup_new\n"); -+ unregister_trace_sched_wakeup( -+ probe_wakeup_latency_hist_start, NULL); -+ return ret; -+ } -+ ret = register_trace_sched_switch( -+ probe_wakeup_latency_hist_stop, NULL); -+ if (ret) { -+ pr_info("wakeup trace: Couldn't assign " -+ "probe_wakeup_latency_hist_stop " -+ "to trace_sched_switch\n"); -+ unregister_trace_sched_wakeup( -+ probe_wakeup_latency_hist_start, NULL); -+ unregister_trace_sched_wakeup_new( -+ probe_wakeup_latency_hist_start, NULL); -+ return ret; -+ } -+ ret = register_trace_sched_migrate_task( -+ probe_sched_migrate_task, NULL); -+ if (ret) { -+ pr_info("wakeup trace: Couldn't assign " -+ "probe_sched_migrate_task " -+ "to trace_sched_migrate_task\n"); -+ unregister_trace_sched_wakeup( -+ probe_wakeup_latency_hist_start, NULL); -+ unregister_trace_sched_wakeup_new( -+ probe_wakeup_latency_hist_start, NULL); -+ unregister_trace_sched_switch( -+ probe_wakeup_latency_hist_stop, NULL); -+ return ret; -+ } -+ break; -+#endif -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ case MISSED_TIMER_OFFSETS: -+ ret = register_trace_hrtimer_interrupt( -+ probe_hrtimer_interrupt, NULL); -+ if (ret) { -+ pr_info("wakeup trace: Couldn't assign " -+ "probe_hrtimer_interrupt " -+ "to trace_hrtimer_interrupt\n"); -+ return ret; -+ } -+ break; -+#endif -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ case TIMERANDWAKEUP_LATENCY: -+ if (!wakeup_latency_enabled_data.enabled || -+ !missed_timer_offsets_enabled_data.enabled) -+ return -EINVAL; -+ break; -+#endif -+ default: -+ break; -+ } -+ } else { -+ switch (ed->latency_type) { -+#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST) -+ case PREEMPTIRQSOFF_LATENCY: -+ { -+ int cpu; -+ -+ unregister_trace_preemptirqsoff_hist( -+ probe_preemptirqsoff_hist, NULL); -+ for_each_online_cpu(cpu) { -+#ifdef CONFIG_INTERRUPT_OFF_HIST -+ per_cpu(hist_irqsoff_counting, -+ cpu) = 0; -+#endif -+#ifdef CONFIG_PREEMPT_OFF_HIST -+ per_cpu(hist_preemptoff_counting, -+ cpu) = 0; -+#endif -+#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) -+ per_cpu(hist_preemptirqsoff_counting, -+ cpu) = 0; -+#endif -+ } -+ } -+ break; -+#endif -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ case WAKEUP_LATENCY: -+ { -+ int cpu; -+ -+ unregister_trace_sched_wakeup( -+ probe_wakeup_latency_hist_start, NULL); -+ unregister_trace_sched_wakeup_new( -+ probe_wakeup_latency_hist_start, NULL); -+ unregister_trace_sched_switch( -+ probe_wakeup_latency_hist_stop, NULL); -+ unregister_trace_sched_migrate_task( -+ probe_sched_migrate_task, NULL); -+ -+ for_each_online_cpu(cpu) { -+ per_cpu(wakeup_task, cpu) = NULL; -+ per_cpu(wakeup_sharedprio, cpu) = 0; -+ } -+ } -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ timerandwakeup_enabled_data.enabled = 0; -+#endif -+ break; -+#endif -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ case MISSED_TIMER_OFFSETS: -+ unregister_trace_hrtimer_interrupt( -+ probe_hrtimer_interrupt, NULL); -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ timerandwakeup_enabled_data.enabled = 0; -+#endif -+ break; -+#endif -+ default: -+ break; -+ } -+ } -+ ed->enabled = enable; -+ return cnt; -+} -+ -+static const struct file_operations latency_hist_reset_fops = { -+ .open = tracing_open_generic, -+ .write = latency_hist_reset, -+}; -+ -+static const struct file_operations enable_fops = { -+ .open = tracing_open_generic, -+ .read = show_enable, -+ .write = do_enable, -+}; -+ -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+static const struct file_operations pid_fops = { -+ .open = tracing_open_generic, -+ .read = show_pid, -+ .write = do_pid, -+}; -+ -+static const struct file_operations maxlatproc_fops = { -+ .open = tracing_open_generic, -+ .read = show_maxlatproc, -+}; -+#endif -+ -+#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST) -+static notrace void probe_preemptirqsoff_hist(void *v, int reason, -+ int starthist) -+{ -+ int cpu = raw_smp_processor_id(); -+ int time_set = 0; -+ -+ if (starthist) { -+ u64 uninitialized_var(start); -+ -+ if (!preempt_count() && !irqs_disabled()) -+ return; -+ -+#ifdef CONFIG_INTERRUPT_OFF_HIST -+ if ((reason == IRQS_OFF || reason == TRACE_START) && -+ !per_cpu(hist_irqsoff_counting, cpu)) { -+ per_cpu(hist_irqsoff_counting, cpu) = 1; -+ start = ftrace_now(cpu); -+ time_set++; -+ per_cpu(hist_irqsoff_start, cpu) = start; -+ } -+#endif -+ -+#ifdef CONFIG_PREEMPT_OFF_HIST -+ if ((reason == PREEMPT_OFF || reason == TRACE_START) && -+ !per_cpu(hist_preemptoff_counting, cpu)) { -+ per_cpu(hist_preemptoff_counting, cpu) = 1; -+ if (!(time_set++)) -+ start = ftrace_now(cpu); -+ per_cpu(hist_preemptoff_start, cpu) = start; -+ } -+#endif -+ -+#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) -+ if (per_cpu(hist_irqsoff_counting, cpu) && -+ per_cpu(hist_preemptoff_counting, cpu) && -+ !per_cpu(hist_preemptirqsoff_counting, cpu)) { -+ per_cpu(hist_preemptirqsoff_counting, cpu) = 1; -+ if (!time_set) -+ start = ftrace_now(cpu); -+ per_cpu(hist_preemptirqsoff_start, cpu) = start; -+ } -+#endif -+ } else { -+ u64 uninitialized_var(stop); -+ -+#ifdef CONFIG_INTERRUPT_OFF_HIST -+ if ((reason == IRQS_ON || reason == TRACE_STOP) && -+ per_cpu(hist_irqsoff_counting, cpu)) { -+ u64 start = per_cpu(hist_irqsoff_start, cpu); -+ -+ stop = ftrace_now(cpu); -+ time_set++; -+ if (start) { -+ long latency = ((long) (stop - start)) / -+ NSECS_PER_USECS; -+ -+ latency_hist(IRQSOFF_LATENCY, cpu, latency, 0, -+ stop, NULL); -+ } -+ per_cpu(hist_irqsoff_counting, cpu) = 0; -+ } -+#endif -+ -+#ifdef CONFIG_PREEMPT_OFF_HIST -+ if ((reason == PREEMPT_ON || reason == TRACE_STOP) && -+ per_cpu(hist_preemptoff_counting, cpu)) { -+ u64 start = per_cpu(hist_preemptoff_start, cpu); -+ -+ if (!(time_set++)) -+ stop = ftrace_now(cpu); -+ if (start) { -+ long latency = ((long) (stop - start)) / -+ NSECS_PER_USECS; -+ -+ latency_hist(PREEMPTOFF_LATENCY, cpu, latency, -+ 0, stop, NULL); -+ } -+ per_cpu(hist_preemptoff_counting, cpu) = 0; -+ } -+#endif -+ -+#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) -+ if ((!per_cpu(hist_irqsoff_counting, cpu) || -+ !per_cpu(hist_preemptoff_counting, cpu)) && -+ per_cpu(hist_preemptirqsoff_counting, cpu)) { -+ u64 start = per_cpu(hist_preemptirqsoff_start, cpu); -+ -+ if (!time_set) -+ stop = ftrace_now(cpu); -+ if (start) { -+ long latency = ((long) (stop - start)) / -+ NSECS_PER_USECS; -+ -+ latency_hist(PREEMPTIRQSOFF_LATENCY, cpu, -+ latency, 0, stop, NULL); -+ } -+ per_cpu(hist_preemptirqsoff_counting, cpu) = 0; -+ } -+#endif -+ } -+} -+#endif -+ -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+static DEFINE_RAW_SPINLOCK(wakeup_lock); -+static notrace void probe_sched_migrate_task(void *v, struct task_struct *task, -+ int cpu) -+{ -+ int old_cpu = task_cpu(task); -+ -+ if (cpu != old_cpu) { -+ unsigned long flags; -+ struct task_struct *cpu_wakeup_task; -+ -+ raw_spin_lock_irqsave(&wakeup_lock, flags); -+ -+ cpu_wakeup_task = per_cpu(wakeup_task, old_cpu); -+ if (task == cpu_wakeup_task) { -+ put_task_struct(cpu_wakeup_task); -+ per_cpu(wakeup_task, old_cpu) = NULL; -+ cpu_wakeup_task = per_cpu(wakeup_task, cpu) = task; -+ get_task_struct(cpu_wakeup_task); -+ } -+ -+ raw_spin_unlock_irqrestore(&wakeup_lock, flags); -+ } -+} -+ -+static notrace void probe_wakeup_latency_hist_start(void *v, -+ struct task_struct *p, int success) -+{ -+ unsigned long flags; -+ struct task_struct *curr = current; -+ int cpu = task_cpu(p); -+ struct task_struct *cpu_wakeup_task; -+ -+ raw_spin_lock_irqsave(&wakeup_lock, flags); -+ -+ cpu_wakeup_task = per_cpu(wakeup_task, cpu); -+ -+ if (wakeup_pid) { -+ if ((cpu_wakeup_task && p->prio == cpu_wakeup_task->prio) || -+ p->prio == curr->prio) -+ per_cpu(wakeup_sharedprio, cpu) = 1; -+ if (likely(wakeup_pid != task_pid_nr(p))) -+ goto out; -+ } else { -+ if (likely(!rt_task(p)) || -+ (cpu_wakeup_task && p->prio > cpu_wakeup_task->prio) || -+ p->prio > curr->prio) -+ goto out; -+ if ((cpu_wakeup_task && p->prio == cpu_wakeup_task->prio) || -+ p->prio == curr->prio) -+ per_cpu(wakeup_sharedprio, cpu) = 1; -+ } -+ -+ if (cpu_wakeup_task) -+ put_task_struct(cpu_wakeup_task); -+ cpu_wakeup_task = per_cpu(wakeup_task, cpu) = p; -+ get_task_struct(cpu_wakeup_task); -+ cpu_wakeup_task->preempt_timestamp_hist = -+ ftrace_now(raw_smp_processor_id()); -+out: -+ raw_spin_unlock_irqrestore(&wakeup_lock, flags); -+} -+ -+static notrace void probe_wakeup_latency_hist_stop(void *v, -+ struct task_struct *prev, struct task_struct *next) -+{ -+ unsigned long flags; -+ int cpu = task_cpu(next); -+ long latency; -+ u64 stop; -+ struct task_struct *cpu_wakeup_task; -+ -+ raw_spin_lock_irqsave(&wakeup_lock, flags); -+ -+ cpu_wakeup_task = per_cpu(wakeup_task, cpu); -+ -+ if (cpu_wakeup_task == NULL) -+ goto out; -+ -+ /* Already running? */ -+ if (unlikely(current == cpu_wakeup_task)) -+ goto out_reset; -+ -+ if (next != cpu_wakeup_task) { -+ if (next->prio < cpu_wakeup_task->prio) -+ goto out_reset; -+ -+ if (next->prio == cpu_wakeup_task->prio) -+ per_cpu(wakeup_sharedprio, cpu) = 1; -+ -+ goto out; -+ } -+ -+ if (current->prio == cpu_wakeup_task->prio) -+ per_cpu(wakeup_sharedprio, cpu) = 1; -+ -+ /* -+ * The task we are waiting for is about to be switched to. -+ * Calculate latency and store it in histogram. -+ */ -+ stop = ftrace_now(raw_smp_processor_id()); -+ -+ latency = ((long) (stop - next->preempt_timestamp_hist)) / -+ NSECS_PER_USECS; -+ -+ if (per_cpu(wakeup_sharedprio, cpu)) { -+ latency_hist(WAKEUP_LATENCY_SHAREDPRIO, cpu, latency, 0, stop, -+ next); -+ per_cpu(wakeup_sharedprio, cpu) = 0; -+ } else { -+ latency_hist(WAKEUP_LATENCY, cpu, latency, 0, stop, next); -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ if (timerandwakeup_enabled_data.enabled) { -+ latency_hist(TIMERANDWAKEUP_LATENCY, cpu, -+ next->timer_offset + latency, next->timer_offset, -+ stop, next); -+ } -+#endif -+ } -+ -+out_reset: -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ next->timer_offset = 0; -+#endif -+ put_task_struct(cpu_wakeup_task); -+ per_cpu(wakeup_task, cpu) = NULL; -+out: -+ raw_spin_unlock_irqrestore(&wakeup_lock, flags); -+} -+#endif -+ -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+static notrace void probe_hrtimer_interrupt(void *v, int cpu, -+ long long latency_ns, struct task_struct *curr, -+ struct task_struct *task) -+{ -+ if (latency_ns <= 0 && task != NULL && rt_task(task) && -+ (task->prio < curr->prio || -+ (task->prio == curr->prio && -+ !cpumask_test_cpu(cpu, task->cpus_ptr)))) { -+ long latency; -+ u64 now; -+ -+ if (missed_timer_offsets_pid) { -+ if (likely(missed_timer_offsets_pid != -+ task_pid_nr(task))) -+ return; -+ } -+ -+ now = ftrace_now(cpu); -+ latency = (long) div_s64(-latency_ns, NSECS_PER_USECS); -+ latency_hist(MISSED_TIMER_OFFSETS, cpu, latency, latency, now, -+ task); -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ task->timer_offset = latency; -+#endif -+ } -+} -+#endif -+ -+static __init int latency_hist_init(void) -+{ -+ struct dentry *latency_hist_root = NULL; -+ struct dentry *dentry; -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ struct dentry *dentry_sharedprio; -+#endif -+ struct dentry *entry; -+ struct dentry *enable_root; -+ int i = 0; -+ struct hist_data *my_hist; -+ char name[64]; -+ char *cpufmt = "CPU%d"; -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ char *cpufmt_maxlatproc = "max_latency-CPU%d"; -+ struct maxlatproc_data *mp = NULL; -+#endif -+ -+ dentry = tracing_init_dentry(); -+ latency_hist_root = debugfs_create_dir(latency_hist_dir_root, dentry); -+ enable_root = debugfs_create_dir("enable", latency_hist_root); -+ -+#ifdef CONFIG_INTERRUPT_OFF_HIST -+ dentry = debugfs_create_dir(irqsoff_hist_dir, latency_hist_root); -+ for_each_possible_cpu(i) { -+ sprintf(name, cpufmt, i); -+ entry = debugfs_create_file(name, 0444, dentry, -+ &per_cpu(irqsoff_hist, i), &latency_hist_fops); -+ my_hist = &per_cpu(irqsoff_hist, i); -+ atomic_set(&my_hist->hist_mode, 1); -+ my_hist->min_lat = LONG_MAX; -+ } -+ entry = debugfs_create_file("reset", 0644, dentry, -+ (void *)IRQSOFF_LATENCY, &latency_hist_reset_fops); -+#endif -+ -+#ifdef CONFIG_PREEMPT_OFF_HIST -+ dentry = debugfs_create_dir(preemptoff_hist_dir, -+ latency_hist_root); -+ for_each_possible_cpu(i) { -+ sprintf(name, cpufmt, i); -+ entry = debugfs_create_file(name, 0444, dentry, -+ &per_cpu(preemptoff_hist, i), &latency_hist_fops); -+ my_hist = &per_cpu(preemptoff_hist, i); -+ atomic_set(&my_hist->hist_mode, 1); -+ my_hist->min_lat = LONG_MAX; -+ } -+ entry = debugfs_create_file("reset", 0644, dentry, -+ (void *)PREEMPTOFF_LATENCY, &latency_hist_reset_fops); -+#endif -+ -+#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) -+ dentry = debugfs_create_dir(preemptirqsoff_hist_dir, -+ latency_hist_root); -+ for_each_possible_cpu(i) { -+ sprintf(name, cpufmt, i); -+ entry = debugfs_create_file(name, 0444, dentry, -+ &per_cpu(preemptirqsoff_hist, i), &latency_hist_fops); -+ my_hist = &per_cpu(preemptirqsoff_hist, i); -+ atomic_set(&my_hist->hist_mode, 1); -+ my_hist->min_lat = LONG_MAX; -+ } -+ entry = debugfs_create_file("reset", 0644, dentry, -+ (void *)PREEMPTIRQSOFF_LATENCY, &latency_hist_reset_fops); -+#endif -+ -+#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST) -+ entry = debugfs_create_file("preemptirqsoff", 0644, -+ enable_root, (void *)&preemptirqsoff_enabled_data, -+ &enable_fops); -+#endif -+ -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ dentry = debugfs_create_dir(wakeup_latency_hist_dir, -+ latency_hist_root); -+ dentry_sharedprio = debugfs_create_dir( -+ wakeup_latency_hist_dir_sharedprio, dentry); -+ for_each_possible_cpu(i) { -+ sprintf(name, cpufmt, i); -+ -+ entry = debugfs_create_file(name, 0444, dentry, -+ &per_cpu(wakeup_latency_hist, i), -+ &latency_hist_fops); -+ my_hist = &per_cpu(wakeup_latency_hist, i); -+ atomic_set(&my_hist->hist_mode, 1); -+ my_hist->min_lat = LONG_MAX; -+ -+ entry = debugfs_create_file(name, 0444, dentry_sharedprio, -+ &per_cpu(wakeup_latency_hist_sharedprio, i), -+ &latency_hist_fops); -+ my_hist = &per_cpu(wakeup_latency_hist_sharedprio, i); -+ atomic_set(&my_hist->hist_mode, 1); -+ my_hist->min_lat = LONG_MAX; -+ -+ sprintf(name, cpufmt_maxlatproc, i); -+ -+ mp = &per_cpu(wakeup_maxlatproc, i); -+ entry = debugfs_create_file(name, 0444, dentry, mp, -+ &maxlatproc_fops); -+ clear_maxlatprocdata(mp); -+ -+ mp = &per_cpu(wakeup_maxlatproc_sharedprio, i); -+ entry = debugfs_create_file(name, 0444, dentry_sharedprio, mp, -+ &maxlatproc_fops); -+ clear_maxlatprocdata(mp); -+ } -+ entry = debugfs_create_file("pid", 0644, dentry, -+ (void *)&wakeup_pid, &pid_fops); -+ entry = debugfs_create_file("reset", 0644, dentry, -+ (void *)WAKEUP_LATENCY, &latency_hist_reset_fops); -+ entry = debugfs_create_file("reset", 0644, dentry_sharedprio, -+ (void *)WAKEUP_LATENCY_SHAREDPRIO, &latency_hist_reset_fops); -+ entry = debugfs_create_file("wakeup", 0644, -+ enable_root, (void *)&wakeup_latency_enabled_data, -+ &enable_fops); -+#endif -+ -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ dentry = debugfs_create_dir(missed_timer_offsets_dir, -+ latency_hist_root); -+ for_each_possible_cpu(i) { -+ sprintf(name, cpufmt, i); -+ entry = debugfs_create_file(name, 0444, dentry, -+ &per_cpu(missed_timer_offsets, i), &latency_hist_fops); -+ my_hist = &per_cpu(missed_timer_offsets, i); -+ atomic_set(&my_hist->hist_mode, 1); -+ my_hist->min_lat = LONG_MAX; -+ -+ sprintf(name, cpufmt_maxlatproc, i); -+ mp = &per_cpu(missed_timer_offsets_maxlatproc, i); -+ entry = debugfs_create_file(name, 0444, dentry, mp, -+ &maxlatproc_fops); -+ clear_maxlatprocdata(mp); -+ } -+ entry = debugfs_create_file("pid", 0644, dentry, -+ (void *)&missed_timer_offsets_pid, &pid_fops); -+ entry = debugfs_create_file("reset", 0644, dentry, -+ (void *)MISSED_TIMER_OFFSETS, &latency_hist_reset_fops); -+ entry = debugfs_create_file("missed_timer_offsets", 0644, -+ enable_root, (void *)&missed_timer_offsets_enabled_data, -+ &enable_fops); -+#endif -+ -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ dentry = debugfs_create_dir(timerandwakeup_latency_hist_dir, -+ latency_hist_root); -+ for_each_possible_cpu(i) { -+ sprintf(name, cpufmt, i); -+ entry = debugfs_create_file(name, 0444, dentry, -+ &per_cpu(timerandwakeup_latency_hist, i), -+ &latency_hist_fops); -+ my_hist = &per_cpu(timerandwakeup_latency_hist, i); -+ atomic_set(&my_hist->hist_mode, 1); -+ my_hist->min_lat = LONG_MAX; -+ -+ sprintf(name, cpufmt_maxlatproc, i); -+ mp = &per_cpu(timerandwakeup_maxlatproc, i); -+ entry = debugfs_create_file(name, 0444, dentry, mp, -+ &maxlatproc_fops); -+ clear_maxlatprocdata(mp); -+ } -+ entry = debugfs_create_file("reset", 0644, dentry, -+ (void *)TIMERANDWAKEUP_LATENCY, &latency_hist_reset_fops); -+ entry = debugfs_create_file("timerandwakeup", 0644, -+ enable_root, (void *)&timerandwakeup_enabled_data, -+ &enable_fops); -+#endif -+ return 0; -+} -+ -+device_initcall(latency_hist_init); ---- a/kernel/trace/trace_irqsoff.c -+++ b/kernel/trace/trace_irqsoff.c -@@ -13,6 +13,7 @@ - #include <linux/uaccess.h> - #include <linux/module.h> - #include <linux/ftrace.h> -+#include <trace/events/hist.h> - - #include "trace.h" - -@@ -436,11 +437,13 @@ void start_critical_timings(void) - { - if (preempt_trace() || irq_trace()) - start_critical_timing(CALLER_ADDR0, CALLER_ADDR1); -+ trace_preemptirqsoff_hist(TRACE_START, 1); - } - EXPORT_SYMBOL_GPL(start_critical_timings); - - void stop_critical_timings(void) - { -+ trace_preemptirqsoff_hist(TRACE_STOP, 0); - if (preempt_trace() || irq_trace()) - stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1); - } -@@ -450,6 +453,7 @@ EXPORT_SYMBOL_GPL(stop_critical_timings) - #ifdef CONFIG_PROVE_LOCKING - void time_hardirqs_on(unsigned long a0, unsigned long a1) - { -+ trace_preemptirqsoff_hist(IRQS_ON, 0); - if (!preempt_trace() && irq_trace()) - stop_critical_timing(a0, a1); - } -@@ -458,6 +462,7 @@ void time_hardirqs_off(unsigned long a0, - { - if (!preempt_trace() && irq_trace()) - start_critical_timing(a0, a1); -+ trace_preemptirqsoff_hist(IRQS_OFF, 1); - } - - #else /* !CONFIG_PROVE_LOCKING */ -@@ -483,6 +488,7 @@ inline void print_irqtrace_events(struct - */ - void trace_hardirqs_on(void) - { -+ trace_preemptirqsoff_hist(IRQS_ON, 0); - if (!preempt_trace() && irq_trace()) - stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1); - } -@@ -492,11 +498,13 @@ void trace_hardirqs_off(void) - { - if (!preempt_trace() && irq_trace()) - start_critical_timing(CALLER_ADDR0, CALLER_ADDR1); -+ trace_preemptirqsoff_hist(IRQS_OFF, 1); - } - EXPORT_SYMBOL(trace_hardirqs_off); - - __visible void trace_hardirqs_on_caller(unsigned long caller_addr) - { -+ trace_preemptirqsoff_hist(IRQS_ON, 0); - if (!preempt_trace() && irq_trace()) - stop_critical_timing(CALLER_ADDR0, caller_addr); - } -@@ -506,6 +514,7 @@ EXPORT_SYMBOL(trace_hardirqs_on_caller); - { - if (!preempt_trace() && irq_trace()) - start_critical_timing(CALLER_ADDR0, caller_addr); -+ trace_preemptirqsoff_hist(IRQS_OFF, 1); - } - EXPORT_SYMBOL(trace_hardirqs_off_caller); - -@@ -515,12 +524,14 @@ EXPORT_SYMBOL(trace_hardirqs_off_caller) - #ifdef CONFIG_PREEMPT_TRACER - void trace_preempt_on(unsigned long a0, unsigned long a1) - { -+ trace_preemptirqsoff_hist(PREEMPT_ON, 0); - if (preempt_trace() && !irq_trace()) - stop_critical_timing(a0, a1); - } - - void trace_preempt_off(unsigned long a0, unsigned long a1) - { -+ trace_preemptirqsoff_hist(PREEMPT_ON, 1); - if (preempt_trace() && !irq_trace()) - start_critical_timing(a0, a1); - } diff --git a/patches/latency_hist-update-sched_wakeup-probe.patch b/patches/latency_hist-update-sched_wakeup-probe.patch deleted file mode 100644 index 60847932f3568..0000000000000 --- a/patches/latency_hist-update-sched_wakeup-probe.patch +++ /dev/null @@ -1,40 +0,0 @@ -Subject: latency_hist: Update sched_wakeup probe -From: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> -Date: Sun, 25 Oct 2015 18:06:05 -0400 - -"sched: Introduce the 'trace_sched_waking' tracepoint" introduces a -prototype change for the sched_wakeup probe: the "success" argument is -removed. Update the latency_hist probe following this change. - -Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> -Cc: Peter Zijlstra (Intel) <peterz@infradead.org> -Cc: Julien Desfossez <jdesfossez@efficios.com> -Cc: Francis Giraldeau <francis.giraldeau@gmail.com> -Cc: Mike Galbraith <efault@gmx.de> -Cc: Steven Rostedt <rostedt@goodmis.org> -Link: http://lkml.kernel.org/r/1445810765-18732-1-git-send-email-mathieu.desnoyers@efficios.com -Signed-off-by: Thomas Gleixner <tglx@linutronix.de> ---- - kernel/trace/latency_hist.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- a/kernel/trace/latency_hist.c -+++ b/kernel/trace/latency_hist.c -@@ -115,7 +115,7 @@ static DEFINE_PER_CPU(struct hist_data, - static char *wakeup_latency_hist_dir = "wakeup"; - static char *wakeup_latency_hist_dir_sharedprio = "sharedprio"; - static notrace void probe_wakeup_latency_hist_start(void *v, -- struct task_struct *p, int success); -+ struct task_struct *p); - static notrace void probe_wakeup_latency_hist_stop(void *v, - struct task_struct *prev, struct task_struct *next); - static notrace void probe_sched_migrate_task(void *, -@@ -869,7 +869,7 @@ static notrace void probe_sched_migrate_ - } - - static notrace void probe_wakeup_latency_hist_start(void *v, -- struct task_struct *p, int success) -+ struct task_struct *p) - { - unsigned long flags; - struct task_struct *curr = current; diff --git a/patches/latencyhist-disable-jump-labels.patch b/patches/latencyhist-disable-jump-labels.patch deleted file mode 100644 index ee5040cd36db3..0000000000000 --- a/patches/latencyhist-disable-jump-labels.patch +++ /dev/null @@ -1,61 +0,0 @@ -From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> -Date: Thu, 4 Feb 2016 14:08:06 +0100 -Subject: latencyhist: disable jump-labels - -Atleast on X86 we die a recursive death - -|CPU: 3 PID: 585 Comm: bash Not tainted 4.4.1-rt4+ #198 -|Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS Debian-1.8.2-1 04/01/2014 -|task: ffff88007ab4cd00 ti: ffff88007ab94000 task.ti: ffff88007ab94000 -|RIP: 0010:[<ffffffff81684870>] [<ffffffff81684870>] int3+0x0/0x10 -|RSP: 0018:ffff88013c107fd8 EFLAGS: 00010082 -|RAX: ffff88007ab4cd00 RBX: ffffffff8100ceab RCX: 0000000080202001 -|RDX: 0000000000000000 RSI: ffffffff8100ceab RDI: ffffffff810c78b2 -|RBP: ffff88007ab97c10 R08: ffffffffff57b000 R09: 0000000000000000 -|R10: ffff88013bb64790 R11: ffff88007ab4cd68 R12: ffffffff8100ceab -|R13: ffffffff810c78b2 R14: ffffffff810f8158 R15: ffffffff810f9120 -|FS: 0000000000000000(0000) GS:ffff88013c100000(0063) knlGS:00000000f74e3940 -|CS: 0010 DS: 002b ES: 002b CR0: 000000008005003b -|CR2: 0000000008cf6008 CR3: 000000013b169000 CR4: 00000000000006e0 -|Call Trace: -| <#DB> -| [<ffffffff810f8158>] ? trace_preempt_off+0x18/0x170 -| <<EOE>> -| [<ffffffff81077745>] preempt_count_add+0xa5/0xc0 -| [<ffffffff810c78b2>] on_each_cpu+0x22/0x90 -| [<ffffffff8100ceab>] text_poke_bp+0x5b/0xc0 -| [<ffffffff8100a29c>] arch_jump_label_transform+0x8c/0xf0 -| [<ffffffff8111c77c>] __jump_label_update+0x6c/0x80 -| [<ffffffff8111c83a>] jump_label_update+0xaa/0xc0 -| [<ffffffff8111ca54>] static_key_slow_inc+0x94/0xa0 -| [<ffffffff810e0d8d>] tracepoint_probe_register_prio+0x26d/0x2c0 -| [<ffffffff810e0df3>] tracepoint_probe_register+0x13/0x20 -| [<ffffffff810fca78>] trace_event_reg+0x98/0xd0 -| [<ffffffff810fcc8b>] __ftrace_event_enable_disable+0x6b/0x180 -| [<ffffffff810fd5b8>] event_enable_write+0x78/0xc0 -| [<ffffffff8117a768>] __vfs_write+0x28/0xe0 -| [<ffffffff8117b025>] vfs_write+0xa5/0x180 -| [<ffffffff8117bb76>] SyS_write+0x46/0xa0 -| [<ffffffff81002c91>] do_fast_syscall_32+0xa1/0x1d0 -| [<ffffffff81684d57>] sysenter_flags_fixed+0xd/0x17 - -during - echo 1 > /sys/kernel/debug/tracing/events/hist/preemptirqsoff_hist/enable - -Reported-By: Christoph Mathys <eraserix@gmail.com> -Cc: stable-rt@vger.kernel.org -Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ---- - arch/Kconfig | 1 + - 1 file changed, 1 insertion(+) - ---- a/arch/Kconfig -+++ b/arch/Kconfig -@@ -55,6 +55,7 @@ config KPROBES - config JUMP_LABEL - bool "Optimize very unlikely/likely branches" - depends on HAVE_ARCH_JUMP_LABEL -+ depends on (!INTERRUPT_OFF_HIST && !PREEMPT_OFF_HIST && !WAKEUP_LATENCY_HIST && !MISSED_TIMER_OFFSETS_HIST) - help - This option enables a transparent branch optimization that - makes certain almost-always-true or almost-always-false branch diff --git a/patches/localversion.patch b/patches/localversion.patch index 03a80b8b0e803..72cdd2b3c7600 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 @@ -+-rt4 ++-rt5 diff --git a/patches/lockdep-Fix-per-cpu-static-objects.patch b/patches/lockdep-Fix-per-cpu-static-objects.patch index f79a7b7dd829b..ec2cea43c7106 100644 --- a/patches/lockdep-Fix-per-cpu-static-objects.patch +++ b/patches/lockdep-Fix-per-cpu-static-objects.patch @@ -1,4 +1,3 @@ -From 8ce371f9846ef1e8b3cc8f6865766cb5c1f17e40 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra <peterz@infradead.org> Date: Mon, 20 Mar 2017 12:26:55 +0100 Subject: [PATCH] lockdep: Fix per-cpu static objects diff --git a/patches/mm-protect-activate-switch-mm.patch b/patches/mm-protect-activate-switch-mm.patch index 6c54732a3a662..14a149684c3c1 100644 --- a/patches/mm-protect-activate-switch-mm.patch +++ b/patches/mm-protect-activate-switch-mm.patch @@ -36,7 +36,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> --- a/fs/exec.c +++ b/fs/exec.c -@@ -1022,12 +1022,14 @@ static int exec_mmap(struct mm_struct *m +@@ -1042,12 +1042,14 @@ static int exec_mmap(struct mm_struct *m } } task_lock(tsk); diff --git a/patches/mm-rt-kmap-atomic-scheduling.patch b/patches/mm-rt-kmap-atomic-scheduling.patch index 9748516d525fb..9ef4f9e4aae9b 100644 --- a/patches/mm-rt-kmap-atomic-scheduling.patch +++ b/patches/mm-rt-kmap-atomic-scheduling.patch @@ -229,7 +229,7 @@ Link: http://lkml.kernel.org/r/1311842631.5890.208.camel@twins /* task_struct member predeclarations (sorted alphabetically): */ struct audit_context; -@@ -1063,6 +1064,12 @@ struct task_struct { +@@ -1058,6 +1059,12 @@ struct task_struct { int softirq_nestcnt; unsigned int softirqs_raised; #endif diff --git a/patches/net-move-xmit_recursion-to-per-task-variable-on-RT.patch b/patches/net-move-xmit_recursion-to-per-task-variable-on-RT.patch index 762f1bfdc9604..30d4f1f47b2f8 100644 --- a/patches/net-move-xmit_recursion-to-per-task-variable-on-RT.patch +++ b/patches/net-move-xmit_recursion-to-per-task-variable-on-RT.patch @@ -80,7 +80,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex); --- a/include/linux/sched.h +++ b/include/linux/sched.h -@@ -1066,6 +1066,9 @@ struct task_struct { +@@ -1061,6 +1061,9 @@ struct task_struct { #ifdef CONFIG_DEBUG_ATOMIC_SLEEP unsigned long task_state_change; #endif diff --git a/patches/oleg-signal-rt-fix.patch b/patches/oleg-signal-rt-fix.patch index 356cf401d281e..4d8c668ccf2e1 100644 --- a/patches/oleg-signal-rt-fix.patch +++ b/patches/oleg-signal-rt-fix.patch @@ -89,7 +89,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> unsigned int sas_ss_flags; --- a/kernel/signal.c +++ b/kernel/signal.c -@@ -1227,8 +1227,8 @@ int do_send_sig_info(int sig, struct sig +@@ -1235,8 +1235,8 @@ int do_send_sig_info(int sig, struct sig * We don't want to have recursive SIGSEGV's etc, for example, * that is why we also clear SIGNAL_UNKILLABLE. */ @@ -100,7 +100,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> { unsigned long int flags; int ret, blocked, ignored; -@@ -1253,6 +1253,39 @@ force_sig_info(int sig, struct siginfo * +@@ -1261,6 +1261,39 @@ force_sig_info(int sig, struct siginfo * return ret; } diff --git a/patches/preempt-lazy-support.patch b/patches/preempt-lazy-support.patch index 0492f4aadda50..8fbff89312528 100644 --- a/patches/preempt-lazy-support.patch +++ b/patches/preempt-lazy-support.patch @@ -140,7 +140,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> --- a/include/linux/sched.h +++ b/include/linux/sched.h -@@ -1514,6 +1514,44 @@ static inline int test_tsk_need_resched( +@@ -1509,6 +1509,44 @@ static inline int test_tsk_need_resched( return unlikely(test_tsk_thread_flag(tsk,TIF_NEED_RESCHED)); } @@ -492,7 +492,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c -@@ -1934,6 +1934,7 @@ tracing_generic_entry_update(struct trac +@@ -1942,6 +1942,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 -@@ -1944,7 +1945,8 @@ tracing_generic_entry_update(struct trac +@@ -1952,7 +1953,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; -@@ -3111,15 +3113,17 @@ get_total_entries(struct trace_buffer *b +@@ -3119,15 +3121,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) -@@ -3145,11 +3149,14 @@ static void print_func_help_header_irq(s +@@ -3153,11 +3157,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/ptrace-fix-ptrace-vs-tasklist_lock-race.patch b/patches/ptrace-fix-ptrace-vs-tasklist_lock-race.patch index 35ae8a1dcc809..ec32f4a999c27 100644 --- a/patches/ptrace-fix-ptrace-vs-tasklist_lock-race.patch +++ b/patches/ptrace-fix-ptrace-vs-tasklist_lock-race.patch @@ -43,7 +43,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> #define task_contributes_to_load(task) ((task->state & TASK_UNINTERRUPTIBLE) != 0 && \ (task->flags & PF_FROZEN) == 0 && \ (task->state & TASK_NOLOAD) == 0) -@@ -1501,6 +1497,51 @@ static inline int test_tsk_need_resched( +@@ -1496,6 +1492,51 @@ static inline int test_tsk_need_resched( return unlikely(test_tsk_thread_flag(tsk,TIF_NEED_RESCHED)); } diff --git a/patches/random-avoid-preempt_disable-ed-section.patch b/patches/random-avoid-preempt_disable-ed-section.patch index 6b426a10f9d72..eef8df002426c 100644 --- a/patches/random-avoid-preempt_disable-ed-section.patch +++ b/patches/random-avoid-preempt_disable-ed-section.patch @@ -1,4 +1,3 @@ -From 81e7296af883a58c3e5609842e129de01442198d Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: Fri, 12 May 2017 15:46:17 +0200 Subject: [PATCH] random: avoid preempt_disable()ed section diff --git a/patches/random-make-it-work-on-rt.patch b/patches/random-make-it-work-on-rt.patch index 8082c554cdf24..b1edc8efd3e36 100644 --- a/patches/random-make-it-work-on-rt.patch +++ b/patches/random-make-it-work-on-rt.patch @@ -20,7 +20,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> --- a/drivers/char/random.c +++ b/drivers/char/random.c -@@ -1102,28 +1102,27 @@ static __u32 get_reg(struct fast_pool *f +@@ -1109,28 +1109,27 @@ static __u32 get_reg(struct fast_pool *f return *(ptr + f->reg_idx++); } diff --git a/patches/rt-introduce-cpu-chill.patch b/patches/rt-introduce-cpu-chill.patch index d484d224ba722..43831e64f37ed 100644 --- a/patches/rt-introduce-cpu-chill.patch +++ b/patches/rt-introduce-cpu-chill.patch @@ -100,7 +100,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> #endif /* defined(_LINUX_DELAY_H) */ --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c -@@ -1741,6 +1741,25 @@ SYSCALL_DEFINE2(nanosleep, struct timesp +@@ -1720,6 +1720,25 @@ SYSCALL_DEFINE2(nanosleep, struct timesp return hrtimer_nanosleep(&tu, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC); } diff --git a/patches/sched-delay-put-task.patch b/patches/sched-delay-put-task.patch index c249eded98040..38f628b5dfca8 100644 --- a/patches/sched-delay-put-task.patch +++ b/patches/sched-delay-put-task.patch @@ -14,7 +14,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> --- a/include/linux/sched.h +++ b/include/linux/sched.h -@@ -1052,6 +1052,9 @@ struct task_struct { +@@ -1047,6 +1047,9 @@ struct task_struct { unsigned int sequential_io; unsigned int sequential_io_avg; #endif diff --git a/patches/sched-rt-mutex-wakeup.patch b/patches/sched-rt-mutex-wakeup.patch index edd5db3ab8042..f3d7e6f1ea0b1 100644 --- a/patches/sched-rt-mutex-wakeup.patch +++ b/patches/sched-rt-mutex-wakeup.patch @@ -26,7 +26,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> void *stack; atomic_t usage; /* Per task flags (PF_*), defined further below: */ -@@ -1415,6 +1417,7 @@ extern struct task_struct *find_task_by_ +@@ -1410,6 +1412,7 @@ extern struct task_struct *find_task_by_ extern int wake_up_state(struct task_struct *tsk, unsigned int state); extern int wake_up_process(struct task_struct *tsk); diff --git a/patches/series b/patches/series index 5037bda44ca81..8547a3f47cda4 100644 --- a/patches/series +++ b/patches/series @@ -183,6 +183,40 @@ 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 + # SCHED BLOCK/WQ block-shorten-interrupt-disabled-regions.patch @@ -248,13 +282,6 @@ x86-io-apic-migra-no-unmask.patch # ANON RW SEMAPHORES -# TRACING -latencyhist-disable-jump-labels.patch -latency-hist.patch -latency_hist-update-sched_wakeup-probe.patch -trace-latency-hist-Consider-new-argument-when-probin.patch -trace_Use_rcuidle_version_for_preemptoff_hist_trace_point.patch - ################################################## # REAL RT STUFF starts here ################################################## diff --git a/patches/signal-fix-up-rcu-wreckage.patch b/patches/signal-fix-up-rcu-wreckage.patch index 7968da84a5565..d67bdfacae0f7 100644 --- a/patches/signal-fix-up-rcu-wreckage.patch +++ b/patches/signal-fix-up-rcu-wreckage.patch @@ -12,7 +12,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> --- a/kernel/signal.c +++ b/kernel/signal.c -@@ -1287,12 +1287,12 @@ struct sighand_struct *__lock_task_sigha +@@ -1295,12 +1295,12 @@ struct sighand_struct *__lock_task_sigha * Disable interrupts early to avoid deadlocks. * See rcu_read_unlock() comment header for details. */ @@ -27,7 +27,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> break; } /* -@@ -1313,7 +1313,7 @@ struct sighand_struct *__lock_task_sigha +@@ -1321,7 +1321,7 @@ struct sighand_struct *__lock_task_sigha } spin_unlock(&sighand->siglock); rcu_read_unlock(); diff --git a/patches/signal-revert-ptrace-preempt-magic.patch b/patches/signal-revert-ptrace-preempt-magic.patch index 0857b62353b10..08ca72063a392 100644 --- a/patches/signal-revert-ptrace-preempt-magic.patch +++ b/patches/signal-revert-ptrace-preempt-magic.patch @@ -13,7 +13,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> --- a/kernel/signal.c +++ b/kernel/signal.c -@@ -1857,15 +1857,7 @@ static void ptrace_stop(int exit_code, i +@@ -1865,15 +1865,7 @@ static void ptrace_stop(int exit_code, i if (gstop_done && ptrace_reparented(current)) do_notify_parent_cldstop(current, false, why); diff --git a/patches/softirq-split-locks.patch b/patches/softirq-split-locks.patch index c5ef4368c6595..c36441c5af8ce 100644 --- a/patches/softirq-split-locks.patch +++ b/patches/softirq-split-locks.patch @@ -172,7 +172,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> (NMI_MASK | HARDIRQ_MASK | SOFTIRQ_OFFSET))) --- a/include/linux/sched.h +++ b/include/linux/sched.h -@@ -1055,6 +1055,8 @@ struct task_struct { +@@ -1050,6 +1050,8 @@ struct task_struct { #endif #ifdef CONFIG_PREEMPT_RT_BASE struct rcu_head put_rcu; @@ -181,7 +181,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> #endif #ifdef CONFIG_DEBUG_ATOMIC_SLEEP unsigned long task_state_change; -@@ -1227,6 +1229,7 @@ extern struct pid *cad_pid; +@@ -1222,6 +1224,7 @@ extern struct pid *cad_pid; /* * Per process flags */ diff --git a/patches/timekeeping-split-jiffies-lock.patch b/patches/timekeeping-split-jiffies-lock.patch index 651d5c104036c..44860444fa46d 100644 --- a/patches/timekeeping-split-jiffies-lock.patch +++ b/patches/timekeeping-split-jiffies-lock.patch @@ -129,7 +129,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> if (rcu_needs_cpu(basemono, &next_rcu) || --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c -@@ -2302,8 +2302,10 @@ EXPORT_SYMBOL(hardpps); +@@ -2323,8 +2323,10 @@ EXPORT_SYMBOL(hardpps); */ void xtime_update(unsigned long ticks) { diff --git a/patches/timer-hrtimer-check-properly-for-a-running-timer.patch b/patches/timer-hrtimer-check-properly-for-a-running-timer.patch index b94a0aa120d84..cc05b42923902 100644 --- a/patches/timer-hrtimer-check-properly-for-a-running-timer.patch +++ b/patches/timer-hrtimer-check-properly-for-a-running-timer.patch @@ -16,7 +16,7 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h -@@ -444,7 +444,13 @@ static inline int hrtimer_is_queued(stru +@@ -440,7 +440,13 @@ static inline int hrtimer_is_queued(stru */ static inline int hrtimer_callback_running(const struct hrtimer *timer) { diff --git a/patches/trace-latency-hist-Consider-new-argument-when-probin.patch b/patches/trace-latency-hist-Consider-new-argument-when-probin.patch deleted file mode 100644 index 1b2550ae79d56..0000000000000 --- a/patches/trace-latency-hist-Consider-new-argument-when-probin.patch +++ /dev/null @@ -1,37 +0,0 @@ -From: Carsten Emde <C.Emde@osadl.org> -Date: Tue, 5 Jan 2016 10:21:59 +0100 -Subject: trace/latency-hist: Consider new argument when probing the - sched_switch tracer - -The sched_switch tracer has got a new argument. Fix the latency tracer -accordingly. - -Recently: c73464b1c843 ("sched/core: Fix trace_sched_switch()") since -v4.4-rc1. - -Signed-off-by: Carsten Emde <C.Emde@osadl.org> -Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> ---- - kernel/trace/latency_hist.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- a/kernel/trace/latency_hist.c -+++ b/kernel/trace/latency_hist.c -@@ -117,7 +117,7 @@ static char *wakeup_latency_hist_dir_sha - static notrace void probe_wakeup_latency_hist_start(void *v, - struct task_struct *p); - static notrace void probe_wakeup_latency_hist_stop(void *v, -- struct task_struct *prev, struct task_struct *next); -+ bool preempt, struct task_struct *prev, struct task_struct *next); - static notrace void probe_sched_migrate_task(void *, - struct task_struct *task, int cpu); - static struct enable_data wakeup_latency_enabled_data = { -@@ -907,7 +907,7 @@ static notrace void probe_wakeup_latency - } - - static notrace void probe_wakeup_latency_hist_stop(void *v, -- struct task_struct *prev, struct task_struct *next) -+ bool preempt, struct task_struct *prev, struct task_struct *next) - { - unsigned long flags; - int cpu = task_cpu(next); diff --git a/patches/trace_Use_rcuidle_version_for_preemptoff_hist_trace_point.patch b/patches/trace_Use_rcuidle_version_for_preemptoff_hist_trace_point.patch deleted file mode 100644 index 25f84301be6f2..0000000000000 --- a/patches/trace_Use_rcuidle_version_for_preemptoff_hist_trace_point.patch +++ /dev/null @@ -1,90 +0,0 @@ -Subject: trace: Use rcuidle version for preemptoff_hist trace point -From: Yang Shi <yang.shi@windriver.com> -Date: Tue, 23 Feb 2016 13:23:23 -0800 - -When running -rt kernel with both PREEMPT_OFF_HIST and LOCKDEP enabled, -the below error is reported: - - [ INFO: suspicious RCU usage. ] - 4.4.1-rt6 #1 Not tainted - include/trace/events/hist.h:31 suspicious rcu_dereference_check() usage! - - other info that might help us debug this: - - RCU used illegally from idle CPU! - rcu_scheduler_active = 1, debug_locks = 0 - RCU used illegally from extended quiescent state! - no locks held by swapper/0/0. - - stack backtrace: - CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.4.1-rt6-WR8.0.0.0_standard #1 - Stack : 0000000000000006 0000000000000000 ffffffff81ca8c38 ffffffff81c8fc80 - ffffffff811bdd68 ffffffff81cb0000 0000000000000000 ffffffff81cb0000 - 0000000000000000 0000000000000000 0000000000000004 0000000000000000 - 0000000000000004 ffffffff811bdf50 0000000000000000 ffffffff82b60000 - 0000000000000000 ffffffff812897ac ffffffff819f0000 000000000000000b - ffffffff811be460 ffffffff81b7c588 ffffffff81c8fc80 0000000000000000 - 0000000000000000 ffffffff81ec7f88 ffffffff81d70000 ffffffff81b70000 - ffffffff81c90000 ffffffff81c3fb00 ffffffff81c3fc28 ffffffff815e6f98 - 0000000000000000 ffffffff81c8fa87 ffffffff81b70958 ffffffff811bf2c4 - 0707fe32e8d60ca5 ffffffff81126d60 0000000000000000 0000000000000000 - ... - Call Trace: - [<ffffffff81126d60>] show_stack+0xe8/0x108 - [<ffffffff815e6f98>] dump_stack+0x88/0xb0 - [<ffffffff8124b88c>] time_hardirqs_off+0x204/0x300 - [<ffffffff811aa5dc>] trace_hardirqs_off_caller+0x24/0xe8 - [<ffffffff811a4ec4>] cpu_startup_entry+0x39c/0x508 - [<ffffffff81d7dc68>] start_kernel+0x584/0x5a0 - -Replace regular trace_preemptoff_hist to rcuidle version to avoid the error. - -Signed-off-by: Yang Shi <yang.shi@windriver.com> -Cc: bigeasy@linutronix.de -Cc: rostedt@goodmis.org -Cc: linux-rt-users@vger.kernel.org -Link: http://lkml.kernel.org/r/1456262603-10075-1-git-send-email-yang.shi@windriver.com -Signed-off-by: Thomas Gleixner <tglx@linutronix.de> ---- -I recall the rcuidle version is used by 4.1-rt, but not sure why it is dropped -in 4.4-rt. It looks such fix is still needed. - - kernel/trace/trace_irqsoff.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - ---- a/kernel/trace/trace_irqsoff.c -+++ b/kernel/trace/trace_irqsoff.c -@@ -437,13 +437,13 @@ void start_critical_timings(void) - { - if (preempt_trace() || irq_trace()) - start_critical_timing(CALLER_ADDR0, CALLER_ADDR1); -- trace_preemptirqsoff_hist(TRACE_START, 1); -+ trace_preemptirqsoff_hist_rcuidle(TRACE_START, 1); - } - EXPORT_SYMBOL_GPL(start_critical_timings); - - void stop_critical_timings(void) - { -- trace_preemptirqsoff_hist(TRACE_STOP, 0); -+ trace_preemptirqsoff_hist_rcuidle(TRACE_STOP, 0); - if (preempt_trace() || irq_trace()) - stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1); - } -@@ -453,7 +453,7 @@ EXPORT_SYMBOL_GPL(stop_critical_timings) - #ifdef CONFIG_PROVE_LOCKING - void time_hardirqs_on(unsigned long a0, unsigned long a1) - { -- trace_preemptirqsoff_hist(IRQS_ON, 0); -+ trace_preemptirqsoff_hist_rcuidle(IRQS_ON, 0); - if (!preempt_trace() && irq_trace()) - stop_critical_timing(a0, a1); - } -@@ -462,7 +462,7 @@ void time_hardirqs_off(unsigned long a0, - { - if (!preempt_trace() && irq_trace()) - start_critical_timing(a0, a1); -- trace_preemptirqsoff_hist(IRQS_OFF, 1); -+ trace_preemptirqsoff_hist_rcuidle(IRQS_OFF, 1); - } - - #else /* !CONFIG_PROVE_LOCKING */ diff --git a/patches/x86-kvm-require-const-tsc-for-rt.patch b/patches/x86-kvm-require-const-tsc-for-rt.patch index 255b5e1181bdf..acfa6b0ca0351 100644 --- a/patches/x86-kvm-require-const-tsc-for-rt.patch +++ b/patches/x86-kvm-require-const-tsc-for-rt.patch @@ -14,7 +14,7 @@ Signed-off-by: Thomas Gleixner <tglx@linutronix.de> --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c -@@ -6105,6 +6105,13 @@ int kvm_arch_init(void *opaque) +@@ -6107,6 +6107,13 @@ int kvm_arch_init(void *opaque) goto out; } |