From be36f169b9ccb34634a84f2cf16d3290702e382b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 23 May 2024 14:40:32 +0200 Subject: [PATCH] 4.19-stable patches added patches: btrfs-add-missing-mutex_unlock-in-btrfs_relocate_sys_chunks.patch docs-kernel_include.py-cope-with-docutils-0.21.patch serial-kgdboc-fix-nmi-safety-problems-from-keyboard-reset-code.patch string.h-add-str_has_prefix-helper-function.patch tracing-add-unified-dynamic-event-framework.patch tracing-consolidate-trace_add-remove_event_call-back-to-the-nolock-functions.patch tracing-generalize-hist-trigger-onmax-and-save-action.patch tracing-have-the-historgram-use-the-result-of-str_has_prefix-for-len-of-prefix.patch tracing-refactor-hist-trigger-action-code.patch tracing-remove-unnecessary-var_ref-destroy-in-track_data_destroy.patch tracing-remove-unneeded-synth_event_mutex.patch tracing-simplify-creation-and-deletion-of-synthetic-events.patch tracing-split-up-onmatch-action-data.patch tracing-use-dyn_event-framework-for-synthetic-events.patch tracing-use-str_has_prefix-helper-for-histogram-code.patch tracing-use-str_has_prefix-instead-of-using-fixed-sizes.patch --- ..._unlock-in-btrfs_relocate_sys_chunks.patch | 36 + ...l_include.py-cope-with-docutils-0.21.patch | 66 ++ ...ty-problems-from-keyboard-reset-code.patch | 95 ++ queue-4.19/series | 16 + ...h-add-str_has_prefix-helper-function.patch | 83 ++ ...-add-unified-dynamic-event-framework.patch | 407 +++++++++ ...nt_call-back-to-the-nolock-functions.patch | 132 +++ ...e-hist-trigger-onmax-and-save-action.patch | 459 ++++++++++ ...-of-str_has_prefix-for-len-of-prefix.patch | 53 ++ ...ng-refactor-hist-trigger-action-code.patch | 851 ++++++++++++++++++ ...ar_ref-destroy-in-track_data_destroy.patch | 107 +++ ...ng-remove-unneeded-synth_event_mutex.patch | 195 ++++ ...ion-and-deletion-of-synthetic-events.patch | 128 +++ ...tracing-split-up-onmatch-action-data.patch | 255 ++++++ ...event-framework-for-synthetic-events.patch | 537 +++++++++++ ...has_prefix-helper-for-histogram-code.patch | 125 +++ ..._prefix-instead-of-using-fixed-sizes.patch | 83 ++ 17 files changed, 3628 insertions(+) create mode 100644 queue-4.19/btrfs-add-missing-mutex_unlock-in-btrfs_relocate_sys_chunks.patch create mode 100644 queue-4.19/docs-kernel_include.py-cope-with-docutils-0.21.patch create mode 100644 queue-4.19/serial-kgdboc-fix-nmi-safety-problems-from-keyboard-reset-code.patch create mode 100644 queue-4.19/string.h-add-str_has_prefix-helper-function.patch create mode 100644 queue-4.19/tracing-add-unified-dynamic-event-framework.patch create mode 100644 queue-4.19/tracing-consolidate-trace_add-remove_event_call-back-to-the-nolock-functions.patch create mode 100644 queue-4.19/tracing-generalize-hist-trigger-onmax-and-save-action.patch create mode 100644 queue-4.19/tracing-have-the-historgram-use-the-result-of-str_has_prefix-for-len-of-prefix.patch create mode 100644 queue-4.19/tracing-refactor-hist-trigger-action-code.patch create mode 100644 queue-4.19/tracing-remove-unnecessary-var_ref-destroy-in-track_data_destroy.patch create mode 100644 queue-4.19/tracing-remove-unneeded-synth_event_mutex.patch create mode 100644 queue-4.19/tracing-simplify-creation-and-deletion-of-synthetic-events.patch create mode 100644 queue-4.19/tracing-split-up-onmatch-action-data.patch create mode 100644 queue-4.19/tracing-use-dyn_event-framework-for-synthetic-events.patch create mode 100644 queue-4.19/tracing-use-str_has_prefix-helper-for-histogram-code.patch create mode 100644 queue-4.19/tracing-use-str_has_prefix-instead-of-using-fixed-sizes.patch diff --git a/queue-4.19/btrfs-add-missing-mutex_unlock-in-btrfs_relocate_sys_chunks.patch b/queue-4.19/btrfs-add-missing-mutex_unlock-in-btrfs_relocate_sys_chunks.patch new file mode 100644 index 00000000000..65d33161505 --- /dev/null +++ b/queue-4.19/btrfs-add-missing-mutex_unlock-in-btrfs_relocate_sys_chunks.patch @@ -0,0 +1,36 @@ +From 9af503d91298c3f2945e73703f0e00995be08c30 Mon Sep 17 00:00:00 2001 +From: Dominique Martinet +Date: Fri, 19 Apr 2024 11:22:48 +0900 +Subject: btrfs: add missing mutex_unlock in btrfs_relocate_sys_chunks() + +From: Dominique Martinet + +commit 9af503d91298c3f2945e73703f0e00995be08c30 upstream. + +The previous patch that replaced BUG_ON by error handling forgot to +unlock the mutex in the error path. + +Link: https://lore.kernel.org/all/Zh%2fHpAGFqa7YAFuM@duo.ucw.cz +Reported-by: Pavel Machek +Fixes: 7411055db5ce ("btrfs: handle chunk tree lookup error in btrfs_relocate_sys_chunks()") +CC: stable@vger.kernel.org +Reviewed-by: Pavel Machek +Signed-off-by: Dominique Martinet +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Dominique Martinet +Signed-off-by: Greg Kroah-Hartman +--- + fs/btrfs/volumes.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/fs/btrfs/volumes.c ++++ b/fs/btrfs/volumes.c +@@ -2957,6 +2957,7 @@ again: + * alignment and size). + */ + ret = -EUCLEAN; ++ mutex_unlock(&fs_info->delete_unused_bgs_mutex); + goto error; + } + diff --git a/queue-4.19/docs-kernel_include.py-cope-with-docutils-0.21.patch b/queue-4.19/docs-kernel_include.py-cope-with-docutils-0.21.patch new file mode 100644 index 00000000000..1ffb97a855f --- /dev/null +++ b/queue-4.19/docs-kernel_include.py-cope-with-docutils-0.21.patch @@ -0,0 +1,66 @@ +From d43ddd5c91802a46354fa4c4381416ef760676e2 Mon Sep 17 00:00:00 2001 +From: Akira Yokosawa +Date: Wed, 1 May 2024 12:16:11 +0900 +Subject: docs: kernel_include.py: Cope with docutils 0.21 + +From: Akira Yokosawa + +commit d43ddd5c91802a46354fa4c4381416ef760676e2 upstream. + +Running "make htmldocs" on a newly installed Sphinx 7.3.7 ends up in +a build error: + + Sphinx parallel build error: + AttributeError: module 'docutils.nodes' has no attribute 'reprunicode' + +docutils 0.21 has removed nodes.reprunicode, quote from release note [1]: + + * Removed objects: + + docutils.nodes.reprunicode, docutils.nodes.ensure_str() + Python 2 compatibility hacks + +Sphinx 7.3.0 supports docutils 0.21 [2]: + +kernel_include.py, whose origin is misc.py of docutils, uses reprunicode. + +Upstream docutils removed the offending line from the corresponding file +(docutils/docutils/parsers/rst/directives/misc.py) in January 2022. +Quoting the changelog [3]: + + Deprecate `nodes.reprunicode` and `nodes.ensure_str()`. + + Drop uses of the deprecated constructs (not required with Python 3). + +Do the same for kernel_include.py. + +Tested against: + - Sphinx 2.4.5 (docutils 0.17.1) + - Sphinx 3.4.3 (docutils 0.17.1) + - Sphinx 5.3.0 (docutils 0.18.1) + - Sphinx 6.2.1 (docutils 0.19) + - Sphinx 7.2.6 (docutils 0.20.1) + - Sphinx 7.3.7 (docutils 0.21.2) + +Link: http://www.docutils.org/RELEASE-NOTES.html#release-0-21-2024-04-09 [1] +Link: https://www.sphinx-doc.org/en/master/changes.html#release-7-3-0-released-apr-16-2024 [2] +Link: https://github.com/docutils/docutils/commit/c8471ce47a24 [3] +Signed-off-by: Akira Yokosawa +Cc: stable@vger.kernel.org +Signed-off-by: Jonathan Corbet +Link: https://lore.kernel.org/r/faf5fa45-2a9d-4573-9d2e-3930bdc1ed65@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + Documentation/sphinx/kernel_include.py | 1 - + 1 file changed, 1 deletion(-) + +--- a/Documentation/sphinx/kernel_include.py ++++ b/Documentation/sphinx/kernel_include.py +@@ -94,7 +94,6 @@ class KernelInclude(Include): + # HINT: this is the only line I had to change / commented out: + #path = utils.relative_path(None, path) + +- path = nodes.reprunicode(path) + encoding = self.options.get( + 'encoding', self.state.document.settings.input_encoding) + e_handler=self.state.document.settings.input_encoding_error_handler diff --git a/queue-4.19/serial-kgdboc-fix-nmi-safety-problems-from-keyboard-reset-code.patch b/queue-4.19/serial-kgdboc-fix-nmi-safety-problems-from-keyboard-reset-code.patch new file mode 100644 index 00000000000..9817b409b9c --- /dev/null +++ b/queue-4.19/serial-kgdboc-fix-nmi-safety-problems-from-keyboard-reset-code.patch @@ -0,0 +1,95 @@ +From b2aba15ad6f908d1a620fd97f6af5620c3639742 Mon Sep 17 00:00:00 2001 +From: Daniel Thompson +Date: Wed, 24 Apr 2024 15:21:41 +0100 +Subject: serial: kgdboc: Fix NMI-safety problems from keyboard reset code + +From: Daniel Thompson + +commit b2aba15ad6f908d1a620fd97f6af5620c3639742 upstream. + +Currently, when kdb is compiled with keyboard support, then we will use +schedule_work() to provoke reset of the keyboard status. Unfortunately +schedule_work() gets called from the kgdboc post-debug-exception +handler. That risks deadlock since schedule_work() is not NMI-safe and, +even on platforms where the NMI is not directly used for debugging, the +debug trap can have NMI-like behaviour depending on where breakpoints +are placed. + +Fix this by using the irq work system, which is NMI-safe, to defer the +call to schedule_work() to a point when it is safe to call. + +Reported-by: Liuye +Closes: https://lore.kernel.org/all/20240228025602.3087748-1-liu.yeC@h3c.com/ +Cc: stable@vger.kernel.org +Reviewed-by: Douglas Anderson +Acked-by: Greg Kroah-Hartman +Link: https://lore.kernel.org/r/20240424-kgdboc_fix_schedule_work-v2-1-50f5a490aec5@linaro.org +Signed-off-by: Daniel Thompson +Signed-off-by: Greg Kroah-Hartman +--- + drivers/tty/serial/kgdboc.c | 30 +++++++++++++++++++++++++++++- + 1 file changed, 29 insertions(+), 1 deletion(-) + +--- a/drivers/tty/serial/kgdboc.c ++++ b/drivers/tty/serial/kgdboc.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + + #define MAX_CONFIG_LEN 40 +@@ -35,6 +36,25 @@ static int kgdboc_use_kms; /* 1 if we u + static struct tty_driver *kgdb_tty_driver; + static int kgdb_tty_line; + ++/* ++ * When we leave the debug trap handler we need to reset the keyboard status ++ * (since the original keyboard state gets partially clobbered by kdb use of ++ * the keyboard). ++ * ++ * The path to deliver the reset is somewhat circuitous. ++ * ++ * To deliver the reset we register an input handler, reset the keyboard and ++ * then deregister the input handler. However, to get this done right, we do ++ * have to carefully manage the calling context because we can only register ++ * input handlers from task context. ++ * ++ * In particular we need to trigger the action from the debug trap handler with ++ * all its NMI and/or NMI-like oddities. To solve this the kgdboc trap exit code ++ * (the "post_exception" callback) uses irq_work_queue(), which is NMI-safe, to ++ * schedule a callback from a hardirq context. From there we have to defer the ++ * work again, this time using schedule_work(), to get a callback using the ++ * system workqueue, which runs in task context. ++ */ + #ifdef CONFIG_KDB_KEYBOARD + static int kgdboc_reset_connect(struct input_handler *handler, + struct input_dev *dev, +@@ -86,10 +106,17 @@ static void kgdboc_restore_input_helper( + + static DECLARE_WORK(kgdboc_restore_input_work, kgdboc_restore_input_helper); + ++static void kgdboc_queue_restore_input_helper(struct irq_work *unused) ++{ ++ schedule_work(&kgdboc_restore_input_work); ++} ++ ++static DEFINE_IRQ_WORK(kgdboc_restore_input_irq_work, kgdboc_queue_restore_input_helper); ++ + static void kgdboc_restore_input(void) + { + if (likely(system_state == SYSTEM_RUNNING)) +- schedule_work(&kgdboc_restore_input_work); ++ irq_work_queue(&kgdboc_restore_input_irq_work); + } + + static int kgdboc_register_kbd(char **cptr) +@@ -120,6 +147,7 @@ static void kgdboc_unregister_kbd(void) + i--; + } + } ++ irq_work_sync(&kgdboc_restore_input_irq_work); + flush_work(&kgdboc_restore_input_work); + } + #else /* ! CONFIG_KDB_KEYBOARD */ diff --git a/queue-4.19/series b/queue-4.19/series index 3d957c42dee..eae924852d5 100644 --- a/queue-4.19/series +++ b/queue-4.19/series @@ -1,2 +1,18 @@ revert-selftests-mm-fix-map_hugetlb-failure-on-64k-page-size-systems.patch dm-limit-the-number-of-targets-and-parameter-size-area.patch +btrfs-add-missing-mutex_unlock-in-btrfs_relocate_sys_chunks.patch +tracing-simplify-creation-and-deletion-of-synthetic-events.patch +tracing-add-unified-dynamic-event-framework.patch +tracing-use-dyn_event-framework-for-synthetic-events.patch +tracing-remove-unneeded-synth_event_mutex.patch +tracing-consolidate-trace_add-remove_event_call-back-to-the-nolock-functions.patch +string.h-add-str_has_prefix-helper-function.patch +tracing-use-str_has_prefix-helper-for-histogram-code.patch +tracing-use-str_has_prefix-instead-of-using-fixed-sizes.patch +tracing-have-the-historgram-use-the-result-of-str_has_prefix-for-len-of-prefix.patch +tracing-refactor-hist-trigger-action-code.patch +tracing-split-up-onmatch-action-data.patch +tracing-generalize-hist-trigger-onmax-and-save-action.patch +tracing-remove-unnecessary-var_ref-destroy-in-track_data_destroy.patch +serial-kgdboc-fix-nmi-safety-problems-from-keyboard-reset-code.patch +docs-kernel_include.py-cope-with-docutils-0.21.patch diff --git a/queue-4.19/string.h-add-str_has_prefix-helper-function.patch b/queue-4.19/string.h-add-str_has_prefix-helper-function.patch new file mode 100644 index 00000000000..74a2d698f93 --- /dev/null +++ b/queue-4.19/string.h-add-str_has_prefix-helper-function.patch @@ -0,0 +1,83 @@ +From stable+bounces-43483-greg=kroah.com@vger.kernel.org Thu May 9 04:30:28 2024 +From: George Guo +Date: Thu, 9 May 2024 10:29:24 +0800 +Subject: string.h: Add str_has_prefix() helper function +To: gregkh@linuxfoundation.org, rostedt@goodmis.org, mhiramat@kernel.org, tom.zanussi@linux.intel.com +Cc: stable@vger.kernel.org, Tom Zanussi , Namhyung Kim , Linus Torvalds , Joe Perches , Andreas Schwab , George Guo +Message-ID: <20240509022931.3513365-7-dongtai.guo@linux.dev> + +From: "Steven Rostedt (VMware)" + +commit 72921427d46bf9731a1ab7864adc64c43dfae29f upstream. + +A discussion came up in the trace triggers thread about converting a +bunch of: + + strncmp(str, "const", sizeof("const") - 1) + +use cases into a helper macro. It started with: + + strncmp(str, const, sizeof(const) - 1) + +But then Joe Perches mentioned that if a const is not used, the +sizeof() will be the size of a pointer, which can be bad. And that +gcc will optimize strlen("const") into "sizeof("const") - 1". + +Thinking about this more, a quick grep in the kernel tree found several +(thousands!) of cases that use this construct. A quick grep also +revealed that there's probably several bugs in that use case. Some are +that people forgot the "- 1" (which I found) and others could be that +the constant for the sizeof is different than the constant (although, I +haven't found any of those, but I also didn't look hard). + +I figured the best thing to do is to create a helper macro and place it +into include/linux/string.h. And go around and fix all the open coded +versions of it later. + +Note, gcc appears to optimize this when we make it into an always_inline +static function, which removes a lot of issues that a macro produces. + +Link: http://lkml.kernel.org/r/e3e754f2bd18e56eaa8baf79bee619316ebf4cfc.1545161087.git.tom.zanussi@linux.intel.com +Link: http://lkml.kernel.org/r/20181219211615.2298e781@gandalf.local.home +Link: http://lkml.kernel.org/r/CAHk-=wg_sR-UEC1ggmkZpypOUYanL5CMX4R7ceuaV4QMf5jBtg@mail.gmail.com + +Cc: Tom Zanussi +Cc: Greg Kroah-Hartman +Acked-by: Namhyung Kim +Suggestions-by: Linus Torvalds +Suggestions-by: Joe Perches +Suggestions-by: Andreas Schwab +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: George Guo +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/string.h | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +--- a/include/linux/string.h ++++ b/include/linux/string.h +@@ -492,4 +492,24 @@ static inline void memcpy_and_pad(void * + memcpy(dest, src, dest_len); + } + ++/** ++ * str_has_prefix - Test if a string has a given prefix ++ * @str: The string to test ++ * @prefix: The string to see if @str starts with ++ * ++ * A common way to test a prefix of a string is to do: ++ * strncmp(str, prefix, sizeof(prefix) - 1) ++ * ++ * But this can lead to bugs due to typos, or if prefix is a pointer ++ * and not a constant. Instead use str_has_prefix(). ++ * ++ * Returns: 0 if @str does not start with @prefix ++ strlen(@prefix) if @str does start with @prefix ++ */ ++static __always_inline size_t str_has_prefix(const char *str, const char *prefix) ++{ ++ size_t len = strlen(prefix); ++ return strncmp(str, prefix, len) == 0 ? len : 0; ++} ++ + #endif /* _LINUX_STRING_H_ */ diff --git a/queue-4.19/tracing-add-unified-dynamic-event-framework.patch b/queue-4.19/tracing-add-unified-dynamic-event-framework.patch new file mode 100644 index 00000000000..193f9777586 --- /dev/null +++ b/queue-4.19/tracing-add-unified-dynamic-event-framework.patch @@ -0,0 +1,407 @@ +From stable+bounces-43479-greg=kroah.com@vger.kernel.org Thu May 9 04:30:16 2024 +From: George Guo +Date: Thu, 9 May 2024 10:29:20 +0800 +Subject: tracing: Add unified dynamic event framework +To: gregkh@linuxfoundation.org, rostedt@goodmis.org, mhiramat@kernel.org, tom.zanussi@linux.intel.com +Cc: stable@vger.kernel.org, George Guo +Message-ID: <20240509022931.3513365-3-dongtai.guo@linux.dev> + +From: Masami Hiramatsu + +commit 5448d44c38557fc15d1c53b608a9c9f0e1ca8f86 upstream. + +Add unified dynamic event framework for ftrace kprobes, uprobes +and synthetic events. Those dynamic events can be co-exist on +same file because those syntax doesn't overlap. + +This introduces a framework part which provides a unified tracefs +interface and operations. + +Link: http://lkml.kernel.org/r/154140852824.17322.12250362185969352095.stgit@devbox + +Reviewed-by: Tom Zanussi +Tested-by: Tom Zanussi +Signed-off-by: Masami Hiramatsu +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: George Guo +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/Kconfig | 3 + kernel/trace/Makefile | 1 + kernel/trace/trace.c | 4 + kernel/trace/trace_dynevent.c | 210 ++++++++++++++++++++++++++++++++++++++++++ + kernel/trace/trace_dynevent.h | 119 +++++++++++++++++++++++ + 5 files changed, 337 insertions(+) + create mode 100644 kernel/trace/trace_dynevent.c + create mode 100644 kernel/trace/trace_dynevent.h + +--- a/kernel/trace/Kconfig ++++ b/kernel/trace/Kconfig +@@ -518,6 +518,9 @@ config BPF_EVENTS + help + This allows the user to attach BPF programs to kprobe events. + ++config DYNAMIC_EVENTS ++ def_bool n ++ + config PROBE_EVENTS + def_bool n + +--- a/kernel/trace/Makefile ++++ b/kernel/trace/Makefile +@@ -78,6 +78,7 @@ endif + ifeq ($(CONFIG_TRACING),y) + obj-$(CONFIG_KGDB_KDB) += trace_kdb.o + endif ++obj-$(CONFIG_DYNAMIC_EVENTS) += trace_dynevent.o + obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o + obj-$(CONFIG_UPROBE_EVENTS) += trace_uprobe.o + +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -4665,6 +4665,10 @@ static const char readme_msg[] = + "\t\t\t traces\n" + #endif + #endif /* CONFIG_STACK_TRACER */ ++#ifdef CONFIG_DYNAMIC_EVENTS ++ " dynamic_events\t\t- Add/remove/show the generic dynamic events\n" ++ "\t\t\t Write into this file to define/undefine new trace events.\n" ++#endif + #ifdef CONFIG_KPROBE_EVENTS + " kprobe_events\t\t- Add/remove/show the kernel dynamic events\n" + "\t\t\t Write into this file to define/undefine new trace events.\n" +--- /dev/null ++++ b/kernel/trace/trace_dynevent.c +@@ -0,0 +1,210 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Generic dynamic event control interface ++ * ++ * Copyright (C) 2018 Masami Hiramatsu ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "trace.h" ++#include "trace_dynevent.h" ++ ++static DEFINE_MUTEX(dyn_event_ops_mutex); ++static LIST_HEAD(dyn_event_ops_list); ++ ++int dyn_event_register(struct dyn_event_operations *ops) ++{ ++ if (!ops || !ops->create || !ops->show || !ops->is_busy || ++ !ops->free || !ops->match) ++ return -EINVAL; ++ ++ INIT_LIST_HEAD(&ops->list); ++ mutex_lock(&dyn_event_ops_mutex); ++ list_add_tail(&ops->list, &dyn_event_ops_list); ++ mutex_unlock(&dyn_event_ops_mutex); ++ return 0; ++} ++ ++int dyn_event_release(int argc, char **argv, struct dyn_event_operations *type) ++{ ++ struct dyn_event *pos, *n; ++ char *system = NULL, *event, *p; ++ int ret = -ENOENT; ++ ++ if (argv[0][1] != ':') ++ return -EINVAL; ++ ++ event = &argv[0][2]; ++ p = strchr(event, '/'); ++ if (p) { ++ system = event; ++ event = p + 1; ++ *p = '\0'; ++ } ++ if (event[0] == '\0') ++ return -EINVAL; ++ ++ mutex_lock(&event_mutex); ++ for_each_dyn_event_safe(pos, n) { ++ if (type && type != pos->ops) ++ continue; ++ if (pos->ops->match(system, event, pos)) { ++ ret = pos->ops->free(pos); ++ break; ++ } ++ } ++ mutex_unlock(&event_mutex); ++ ++ return ret; ++} ++ ++static int create_dyn_event(int argc, char **argv) ++{ ++ struct dyn_event_operations *ops; ++ int ret; ++ ++ if (argv[0][0] == '-') ++ return dyn_event_release(argc, argv, NULL); ++ ++ mutex_lock(&dyn_event_ops_mutex); ++ list_for_each_entry(ops, &dyn_event_ops_list, list) { ++ ret = ops->create(argc, (const char **)argv); ++ if (!ret || ret != -ECANCELED) ++ break; ++ } ++ mutex_unlock(&dyn_event_ops_mutex); ++ if (ret == -ECANCELED) ++ ret = -EINVAL; ++ ++ return ret; ++} ++ ++/* Protected by event_mutex */ ++LIST_HEAD(dyn_event_list); ++ ++void *dyn_event_seq_start(struct seq_file *m, loff_t *pos) ++{ ++ mutex_lock(&event_mutex); ++ return seq_list_start(&dyn_event_list, *pos); ++} ++ ++void *dyn_event_seq_next(struct seq_file *m, void *v, loff_t *pos) ++{ ++ return seq_list_next(v, &dyn_event_list, pos); ++} ++ ++void dyn_event_seq_stop(struct seq_file *m, void *v) ++{ ++ mutex_unlock(&event_mutex); ++} ++ ++static int dyn_event_seq_show(struct seq_file *m, void *v) ++{ ++ struct dyn_event *ev = v; ++ ++ if (ev && ev->ops) ++ return ev->ops->show(m, ev); ++ ++ return 0; ++} ++ ++static const struct seq_operations dyn_event_seq_op = { ++ .start = dyn_event_seq_start, ++ .next = dyn_event_seq_next, ++ .stop = dyn_event_seq_stop, ++ .show = dyn_event_seq_show ++}; ++ ++/* ++ * dyn_events_release_all - Release all specific events ++ * @type: the dyn_event_operations * which filters releasing events ++ * ++ * This releases all events which ->ops matches @type. If @type is NULL, ++ * all events are released. ++ * Return -EBUSY if any of them are in use, and return other errors when ++ * it failed to free the given event. Except for -EBUSY, event releasing ++ * process will be aborted at that point and there may be some other ++ * releasable events on the list. ++ */ ++int dyn_events_release_all(struct dyn_event_operations *type) ++{ ++ struct dyn_event *ev, *tmp; ++ int ret = 0; ++ ++ mutex_lock(&event_mutex); ++ for_each_dyn_event(ev) { ++ if (type && ev->ops != type) ++ continue; ++ if (ev->ops->is_busy(ev)) { ++ ret = -EBUSY; ++ goto out; ++ } ++ } ++ for_each_dyn_event_safe(ev, tmp) { ++ if (type && ev->ops != type) ++ continue; ++ ret = ev->ops->free(ev); ++ if (ret) ++ break; ++ } ++out: ++ mutex_unlock(&event_mutex); ++ ++ return ret; ++} ++ ++static int dyn_event_open(struct inode *inode, struct file *file) ++{ ++ int ret; ++ ++ if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { ++ ret = dyn_events_release_all(NULL); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return seq_open(file, &dyn_event_seq_op); ++} ++ ++static ssize_t dyn_event_write(struct file *file, const char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ return trace_parse_run_command(file, buffer, count, ppos, ++ create_dyn_event); ++} ++ ++static const struct file_operations dynamic_events_ops = { ++ .owner = THIS_MODULE, ++ .open = dyn_event_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release, ++ .write = dyn_event_write, ++}; ++ ++/* Make a tracefs interface for controlling dynamic events */ ++static __init int init_dynamic_event(void) ++{ ++ struct dentry *d_tracer; ++ struct dentry *entry; ++ ++ d_tracer = tracing_init_dentry(); ++ if (IS_ERR(d_tracer)) ++ return 0; ++ ++ entry = tracefs_create_file("dynamic_events", 0644, d_tracer, ++ NULL, &dynamic_events_ops); ++ ++ /* Event list interface */ ++ if (!entry) ++ pr_warn("Could not create tracefs 'dynamic_events' entry\n"); ++ ++ return 0; ++} ++fs_initcall(init_dynamic_event); +--- /dev/null ++++ b/kernel/trace/trace_dynevent.h +@@ -0,0 +1,119 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Common header file for generic dynamic events. ++ */ ++ ++#ifndef _TRACE_DYNEVENT_H ++#define _TRACE_DYNEVENT_H ++ ++#include ++#include ++#include ++#include ++ ++#include "trace.h" ++ ++struct dyn_event; ++ ++/** ++ * struct dyn_event_operations - Methods for each type of dynamic events ++ * ++ * These methods must be set for each type, since there is no default method. ++ * Before using this for dyn_event_init(), it must be registered by ++ * dyn_event_register(). ++ * ++ * @create: Parse and create event method. This is invoked when user passes ++ * a event definition to dynamic_events interface. This must not destruct ++ * the arguments and return -ECANCELED if given arguments doesn't match its ++ * command prefix. ++ * @show: Showing method. This is invoked when user reads the event definitions ++ * via dynamic_events interface. ++ * @is_busy: Check whether given event is busy so that it can not be deleted. ++ * Return true if it is busy, otherwides false. ++ * @free: Delete the given event. Return 0 if success, otherwides error. ++ * @match: Check whether given event and system name match this event. ++ * Return true if it matches, otherwides false. ++ * ++ * Except for @create, these methods are called under holding event_mutex. ++ */ ++struct dyn_event_operations { ++ struct list_head list; ++ int (*create)(int argc, const char *argv[]); ++ int (*show)(struct seq_file *m, struct dyn_event *ev); ++ bool (*is_busy)(struct dyn_event *ev); ++ int (*free)(struct dyn_event *ev); ++ bool (*match)(const char *system, const char *event, ++ struct dyn_event *ev); ++}; ++ ++/* Register new dyn_event type -- must be called at first */ ++int dyn_event_register(struct dyn_event_operations *ops); ++ ++/** ++ * struct dyn_event - Dynamic event list header ++ * ++ * The dyn_event structure encapsulates a list and a pointer to the operators ++ * for making a global list of dynamic events. ++ * User must includes this in each event structure, so that those events can ++ * be added/removed via dynamic_events interface. ++ */ ++struct dyn_event { ++ struct list_head list; ++ struct dyn_event_operations *ops; ++}; ++ ++extern struct list_head dyn_event_list; ++ ++static inline ++int dyn_event_init(struct dyn_event *ev, struct dyn_event_operations *ops) ++{ ++ if (!ev || !ops) ++ return -EINVAL; ++ ++ INIT_LIST_HEAD(&ev->list); ++ ev->ops = ops; ++ return 0; ++} ++ ++static inline int dyn_event_add(struct dyn_event *ev) ++{ ++ lockdep_assert_held(&event_mutex); ++ ++ if (!ev || !ev->ops) ++ return -EINVAL; ++ ++ list_add_tail(&ev->list, &dyn_event_list); ++ return 0; ++} ++ ++static inline void dyn_event_remove(struct dyn_event *ev) ++{ ++ lockdep_assert_held(&event_mutex); ++ list_del_init(&ev->list); ++} ++ ++void *dyn_event_seq_start(struct seq_file *m, loff_t *pos); ++void *dyn_event_seq_next(struct seq_file *m, void *v, loff_t *pos); ++void dyn_event_seq_stop(struct seq_file *m, void *v); ++int dyn_events_release_all(struct dyn_event_operations *type); ++int dyn_event_release(int argc, char **argv, struct dyn_event_operations *type); ++ ++/* ++ * for_each_dyn_event - iterate over the dyn_event list ++ * @pos: the struct dyn_event * to use as a loop cursor ++ * ++ * This is just a basement of for_each macro. Wrap this for ++ * each actual event structure with ops filtering. ++ */ ++#define for_each_dyn_event(pos) \ ++ list_for_each_entry(pos, &dyn_event_list, list) ++ ++/* ++ * for_each_dyn_event - iterate over the dyn_event list safely ++ * @pos: the struct dyn_event * to use as a loop cursor ++ * @n: the struct dyn_event * to use as temporary storage ++ */ ++#define for_each_dyn_event_safe(pos, n) \ ++ list_for_each_entry_safe(pos, n, &dyn_event_list, list) ++ ++#endif diff --git a/queue-4.19/tracing-consolidate-trace_add-remove_event_call-back-to-the-nolock-functions.patch b/queue-4.19/tracing-consolidate-trace_add-remove_event_call-back-to-the-nolock-functions.patch new file mode 100644 index 00000000000..dbdabf0c3b2 --- /dev/null +++ b/queue-4.19/tracing-consolidate-trace_add-remove_event_call-back-to-the-nolock-functions.patch @@ -0,0 +1,132 @@ +From stable+bounces-43482-greg=kroah.com@vger.kernel.org Thu May 9 04:30:26 2024 +From: George Guo +Date: Thu, 9 May 2024 10:29:23 +0800 +Subject: tracing: Consolidate trace_add/remove_event_call back to the nolock functions +To: gregkh@linuxfoundation.org, rostedt@goodmis.org, mhiramat@kernel.org, tom.zanussi@linux.intel.com +Cc: stable@vger.kernel.org, George Guo +Message-ID: <20240509022931.3513365-6-dongtai.guo@linux.dev> + +From: "Steven Rostedt (VMware)" + +commit 7e1413edd6194a9807aa5f3ac0378b9b4b9da879 upstream. + +The trace_add/remove_event_call_nolock() functions were added to allow +the tace_add/remove_event_call() code be called when the event_mutex +lock was already taken. Now that all callers are done within the +event_mutex, there's no reason to have two different interfaces. + +Remove the current wrapper trace_add/remove_event_call()s and rename the +_nolock versions back to the original names. + +Link: http://lkml.kernel.org/r/154140866955.17322.2081425494660638846.stgit@devbox + +Acked-by: Masami Hiramatsu +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: George Guo +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/trace_events.h | 2 -- + kernel/trace/trace_events.c | 30 ++++-------------------------- + kernel/trace/trace_events_hist.c | 6 +++--- + 3 files changed, 7 insertions(+), 31 deletions(-) + +--- a/include/linux/trace_events.h ++++ b/include/linux/trace_events.h +@@ -529,8 +529,6 @@ extern int trace_event_raw_init(struct t + extern int trace_define_field(struct trace_event_call *call, const char *type, + const char *name, int offset, int size, + int is_signed, int filter_type); +-extern int trace_add_event_call_nolock(struct trace_event_call *call); +-extern int trace_remove_event_call_nolock(struct trace_event_call *call); + extern int trace_add_event_call(struct trace_event_call *call); + extern int trace_remove_event_call(struct trace_event_call *call); + extern int trace_event_get_offsets(struct trace_event_call *call); +--- a/kernel/trace/trace_events.c ++++ b/kernel/trace/trace_events.c +@@ -2312,7 +2312,8 @@ __trace_early_add_new_event(struct trace + struct ftrace_module_file_ops; + static void __add_event_to_tracers(struct trace_event_call *call); + +-int trace_add_event_call_nolock(struct trace_event_call *call) ++/* Add an additional event_call dynamically */ ++int trace_add_event_call(struct trace_event_call *call) + { + int ret; + lockdep_assert_held(&event_mutex); +@@ -2327,17 +2328,6 @@ int trace_add_event_call_nolock(struct t + return ret; + } + +-/* Add an additional event_call dynamically */ +-int trace_add_event_call(struct trace_event_call *call) +-{ +- int ret; +- +- mutex_lock(&event_mutex); +- ret = trace_add_event_call_nolock(call); +- mutex_unlock(&event_mutex); +- return ret; +-} +- + /* + * Must be called under locking of trace_types_lock, event_mutex and + * trace_event_sem. +@@ -2383,8 +2373,8 @@ static int probe_remove_event_call(struc + return 0; + } + +-/* no event_mutex version */ +-int trace_remove_event_call_nolock(struct trace_event_call *call) ++/* Remove an event_call */ ++int trace_remove_event_call(struct trace_event_call *call) + { + int ret; + +@@ -2398,18 +2388,6 @@ int trace_remove_event_call_nolock(struc + + return ret; + } +- +-/* Remove an event_call */ +-int trace_remove_event_call(struct trace_event_call *call) +-{ +- int ret; +- +- mutex_lock(&event_mutex); +- ret = trace_remove_event_call_nolock(call); +- mutex_unlock(&event_mutex); +- +- return ret; +-} + + #define for_each_event(event, start, end) \ + for (event = start; \ +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -980,7 +980,7 @@ static int register_synth_event(struct s + call->data = event; + call->tp = event->tp; + +- ret = trace_add_event_call_nolock(call); ++ ret = trace_add_event_call(call); + if (ret) { + pr_warn("Failed to register synthetic event: %s\n", + trace_event_name(call)); +@@ -989,7 +989,7 @@ static int register_synth_event(struct s + + ret = set_synth_event_print_fmt(call); + if (ret < 0) { +- trace_remove_event_call_nolock(call); ++ trace_remove_event_call(call); + goto err; + } + out: +@@ -1004,7 +1004,7 @@ static int unregister_synth_event(struct + struct trace_event_call *call = &event->call; + int ret; + +- ret = trace_remove_event_call_nolock(call); ++ ret = trace_remove_event_call(call); + + return ret; + } diff --git a/queue-4.19/tracing-generalize-hist-trigger-onmax-and-save-action.patch b/queue-4.19/tracing-generalize-hist-trigger-onmax-and-save-action.patch new file mode 100644 index 00000000000..dbc981396db --- /dev/null +++ b/queue-4.19/tracing-generalize-hist-trigger-onmax-and-save-action.patch @@ -0,0 +1,459 @@ +From stable+bounces-43489-greg=kroah.com@vger.kernel.org Thu May 9 04:30:50 2024 +From: George Guo +Date: Thu, 9 May 2024 10:29:30 +0800 +Subject: tracing: Generalize hist trigger onmax and save action +To: gregkh@linuxfoundation.org, rostedt@goodmis.org, mhiramat@kernel.org, tom.zanussi@linux.intel.com +Cc: stable@vger.kernel.org, George Guo +Message-ID: <20240509022931.3513365-13-dongtai.guo@linux.dev> + +From: Tom Zanussi + +commit 466f4528fbc692ea56deca278fa6aeb79e6e8b21 upstream. + +The action refactor code allowed actions and handlers to be separated, +but the existing onmax handler and save action code is still not +flexible enough to handle arbitrary coupling. This change generalizes +them and in the process makes additional handlers and actions easier +to implement. + +The onmax action can be broken up and thought of as two separate +components - a variable to be tracked (the parameter given to the +onmax($var_to_track) function) and an invisible variable created to +save the ongoing result of doing something with that variable, such as +saving the max value of that variable so far seen. + +Separating it out like this and renaming it appropriately allows us to +use the same code for similar tracking functions such as +onchange($var_to_track), which would just track the last value seen +rather than the max seen so far, which is useful in some situations. + +Additionally, because different handlers and actions may want to save +and access data differently e.g. save and retrieve tracking values as +local variables vs something more global, save_val() and get_val() +interface functions are introduced and max-specific implementations +are used instead. + +The same goes for the code that checks whether a maximum has been hit +- a generic check_val() interface and max-checking implementation is +used instead, which allows future patches to make use of he same code +using their own implemetations of similar functionality. + +Link: http://lkml.kernel.org/r/980ea73dd8e3f36db3d646f99652f8fed42b77d4.1550100284.git.tom.zanussi@linux.intel.com + +Signed-off-by: Tom Zanussi +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: George Guo +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/trace_events_hist.c | 242 ++++++++++++++++++++++++++------------- + 1 file changed, 163 insertions(+), 79 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -360,6 +360,8 @@ typedef void (*action_fn_t) (struct hist + struct ring_buffer_event *rbe, void *key, + struct action_data *data, u64 *var_ref_vals); + ++typedef bool (*check_track_val_fn_t) (u64 track_val, u64 var_val); ++ + enum handler_id { + HANDLER_ONMATCH = 1, + HANDLER_ONMAX, +@@ -397,15 +399,35 @@ struct action_data { + } match_data; + + struct { ++ /* ++ * var_str contains the $-unstripped variable ++ * name referenced by var_ref, and used when ++ * printing the action. Because var_ref ++ * creation is deferred to create_actions(), ++ * we need a per-action way to save it until ++ * then, thus var_str. ++ */ + char *var_str; +- unsigned int max_var_ref_idx; +- struct hist_field *max_var; +- struct hist_field *var; +- } onmax; ++ ++ /* ++ * var_ref refers to the variable being ++ * tracked e.g onmax($var). ++ */ ++ struct hist_field *var_ref; ++ ++ /* ++ * track_var contains the 'invisible' tracking ++ * variable created to keep the current ++ * e.g. max value. ++ */ ++ struct hist_field *track_var; ++ ++ check_track_val_fn_t check_val; ++ action_fn_t save_data; ++ } track_data; + }; + }; + +- + static char last_hist_cmd[MAX_FILTER_STR_VAL]; + static char hist_err_str[MAX_FILTER_STR_VAL]; + +@@ -3311,10 +3333,10 @@ 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) ++static void save_track_data_vars(struct hist_trigger_data *hist_data, ++ struct tracing_map_elt *elt, void *rec, ++ struct ring_buffer_event *rbe, void *key, ++ struct action_data *data, u64 *var_ref_vals) + { + __update_field_vars(elt, rbe, rec, hist_data->save_vars, + hist_data->n_save_vars, hist_data->n_field_var_str); +@@ -3452,14 +3474,67 @@ create_target_field_var(struct hist_trig + return create_field_var(target_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) ++static bool check_track_val_max(u64 track_val, u64 var_val) ++{ ++ if (var_val <= track_val) ++ return false; ++ ++ return true; ++} ++ ++static u64 get_track_val(struct hist_trigger_data *hist_data, ++ struct tracing_map_elt *elt, ++ struct action_data *data) ++{ ++ unsigned int track_var_idx = data->track_data.track_var->var.idx; ++ u64 track_val; ++ ++ track_val = tracing_map_read_var(elt, track_var_idx); ++ ++ return track_val; ++} ++ ++static void save_track_val(struct hist_trigger_data *hist_data, ++ struct tracing_map_elt *elt, ++ struct action_data *data, u64 var_val) ++{ ++ unsigned int track_var_idx = data->track_data.track_var->var.idx; ++ ++ tracing_map_set_var(elt, track_var_idx, var_val); ++} ++ ++static void save_track_data(struct hist_trigger_data *hist_data, ++ struct tracing_map_elt *elt, void *rec, ++ struct ring_buffer_event *rbe, void *key, ++ struct action_data *data, u64 *var_ref_vals) ++{ ++ if (data->track_data.save_data) ++ data->track_data.save_data(hist_data, elt, rec, rbe, key, data, var_ref_vals); ++} ++ ++static bool check_track_val(struct tracing_map_elt *elt, ++ struct action_data *data, ++ u64 var_val) + { +- unsigned int i, save_var_idx, max_idx = data->onmax.max_var->var.idx; ++ struct hist_trigger_data *hist_data; ++ u64 track_val; ++ ++ hist_data = data->track_data.track_var->hist_data; ++ track_val = get_track_val(hist_data, elt, data); ++ ++ return data->track_data.check_val(track_val, var_val); ++} ++ ++static void track_data_print(struct seq_file *m, ++ struct hist_trigger_data *hist_data, ++ struct tracing_map_elt *elt, ++ struct action_data *data) ++{ ++ u64 track_val = get_track_val(hist_data, elt, data); ++ unsigned int i, save_var_idx; + +- seq_printf(m, "\n\tmax: %10llu", tracing_map_read_var(elt, max_idx)); ++ if (data->handler == HANDLER_ONMAX) ++ seq_printf(m, "\n\tmax: %10llu", track_val); + + for (i = 0; i < hist_data->n_save_vars; i++) { + struct hist_field *save_val = hist_data->save_vars[i]->val; +@@ -3478,25 +3553,17 @@ static void onmax_print(struct seq_file + } + } + +-static void onmax_save(struct hist_trigger_data *hist_data, +- struct tracing_map_elt *elt, void *rec, +- struct ring_buffer_event *rbe, void *key, +- struct action_data *data, u64 *var_ref_vals) +-{ +- unsigned int max_idx = data->onmax.max_var->var.idx; +- unsigned int max_var_ref_idx = data->onmax.max_var_ref_idx; +- +- u64 var_val, max_val; +- +- 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 ontrack_action(struct hist_trigger_data *hist_data, ++ struct tracing_map_elt *elt, void *rec, ++ struct ring_buffer_event *rbe, void *key, ++ struct action_data *data, u64 *var_ref_vals) ++{ ++ u64 var_val = var_ref_vals[data->track_data.var_ref->var_ref_idx]; ++ ++ if (check_track_val(elt, data, var_val)) { ++ save_track_val(hist_data, elt, data, var_val); ++ save_track_data(hist_data, elt, rec, rbe, key, data, var_ref_vals); ++ } + } + + static void action_data_destroy(struct action_data *data) +@@ -3516,12 +3583,13 @@ static void action_data_destroy(struct a + kfree(data); + } + +-static void onmax_destroy(struct action_data *data) ++static void track_data_destroy(struct hist_trigger_data *hist_data, ++ struct action_data *data) + { +- destroy_hist_field(data->onmax.max_var, 0); +- destroy_hist_field(data->onmax.var, 0); ++ destroy_hist_field(data->track_data.track_var, 0); ++ destroy_hist_field(data->track_data.var_ref, 0); + +- kfree(data->onmax.var_str); ++ kfree(data->track_data.var_str); + + action_data_destroy(data); + } +@@ -3529,25 +3597,24 @@ static void onmax_destroy(struct action_ + static int action_create(struct hist_trigger_data *hist_data, + struct action_data *data); + +-static int onmax_create(struct hist_trigger_data *hist_data, +- struct action_data *data) ++static int track_data_create(struct hist_trigger_data *hist_data, ++ struct action_data *data) + { +- struct hist_field *var_field, *ref_field, *max_var = NULL; ++ struct hist_field *var_field, *ref_field, *track_var = NULL; + struct trace_event_file *file = hist_data->event_file; +- unsigned int var_ref_idx = hist_data->n_var_refs; +- char *onmax_var_str; ++ char *track_data_var_str; + int ret = 0; + +- onmax_var_str = data->onmax.var_str; +- if (onmax_var_str[0] != '$') { +- hist_err("onmax: For onmax(x), x must be a variable: ", onmax_var_str); ++ track_data_var_str = data->track_data.var_str; ++ if (track_data_var_str[0] != '$') { ++ hist_err("For onmax(x), x must be a variable: ", track_data_var_str); + return -EINVAL; + } +- onmax_var_str++; ++ track_data_var_str++; + +- var_field = find_target_event_var(hist_data, NULL, NULL, onmax_var_str); ++ var_field = find_target_event_var(hist_data, NULL, NULL, track_data_var_str); + if (!var_field) { +- hist_err("onmax: Couldn't find onmax variable: ", onmax_var_str); ++ hist_err("Couldn't find onmax variable: ", track_data_var_str); + return -EINVAL; + } + +@@ -3555,17 +3622,16 @@ static int onmax_create(struct hist_trig + if (!ref_field) + return -ENOMEM; + +- data->onmax.var = ref_field; +- +- data->onmax.max_var_ref_idx = var_ref_idx; ++ data->track_data.var_ref = ref_field; + +- 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); ++ if (data->handler == HANDLER_ONMAX) ++ track_var = create_var(hist_data, file, "__max", sizeof(u64), "u64"); ++ if (IS_ERR(track_var)) { ++ hist_err("Couldn't create onmax variable: ", "__max"); ++ ret = PTR_ERR(track_var); + goto out; + } +- data->onmax.max_var = max_var; ++ data->track_data.track_var = track_var; + + ret = action_create(hist_data, data); + out: +@@ -3643,8 +3709,15 @@ static int action_parse(char *str, struc + goto out; + + if (handler == HANDLER_ONMAX) +- data->fn = onmax_save; ++ data->track_data.check_val = check_track_val_max; ++ else { ++ hist_err("action parsing: Handler doesn't support action: ", action_name); ++ ret = -EINVAL; ++ goto out; ++ } + ++ data->track_data.save_data = save_track_data_vars; ++ data->fn = ontrack_action; + data->action = ACTION_SAVE; + } else { + char *params = strsep(&str, ")"); +@@ -3655,7 +3728,15 @@ static int action_parse(char *str, struc + goto out; + } + +- data->fn = action_trace; ++ if (handler == HANDLER_ONMAX) ++ data->track_data.check_val = check_track_val_max; ++ ++ if (handler != HANDLER_ONMATCH) { ++ data->track_data.save_data = action_trace; ++ data->fn = ontrack_action; ++ } else ++ data->fn = action_trace; ++ + data->action = ACTION_TRACE; + } + +@@ -3670,24 +3751,25 @@ static int action_parse(char *str, struc + return ret; + } + +-static struct action_data *onmax_parse(char *str, enum handler_id handler) ++static struct action_data *track_data_parse(struct hist_trigger_data *hist_data, ++ char *str, enum handler_id handler) + { + struct action_data *data; +- char *onmax_var_str; + int ret = -EINVAL; ++ char *var_str; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + +- onmax_var_str = strsep(&str, ")"); +- if (!onmax_var_str || !str) { ++ var_str = strsep(&str, ")"); ++ if (!var_str || !str) { + ret = -EINVAL; + goto free; + } + +- data->onmax.var_str = kstrdup(onmax_var_str, GFP_KERNEL); +- if (!data->onmax.var_str) { ++ data->track_data.var_str = kstrdup(var_str, GFP_KERNEL); ++ if (!data->track_data.var_str) { + ret = -ENOMEM; + goto free; + } +@@ -3698,7 +3780,7 @@ static struct action_data *onmax_parse(c + out: + return data; + free: +- onmax_destroy(data); ++ track_data_destroy(hist_data, data); + data = ERR_PTR(ret); + goto out; + } +@@ -4465,7 +4547,7 @@ static void destroy_actions(struct hist_ + if (data->handler == HANDLER_ONMATCH) + onmatch_destroy(data); + else if (data->handler == HANDLER_ONMAX) +- onmax_destroy(data); ++ track_data_destroy(hist_data, data); + else + kfree(data); + } +@@ -4494,7 +4576,8 @@ static int parse_actions(struct hist_tri + } else if ((len = str_has_prefix(str, "onmax("))) { + char *action_str = str + len; + +- data = onmax_parse(action_str, HANDLER_ONMAX); ++ data = track_data_parse(hist_data, action_str, ++ HANDLER_ONMAX); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + break; +@@ -4524,7 +4607,7 @@ static int create_actions(struct hist_tr + if (ret) + break; + } else if (data->handler == HANDLER_ONMAX) { +- ret = onmax_create(hist_data, data); ++ ret = track_data_create(hist_data, data); + if (ret) + break; + } else { +@@ -4546,7 +4629,7 @@ static void print_actions(struct seq_fil + struct action_data *data = hist_data->actions[i]; + + if (data->handler == HANDLER_ONMAX) +- onmax_print(m, hist_data, elt, data); ++ track_data_print(m, hist_data, elt, data); + } + } + +@@ -4571,12 +4654,13 @@ static void print_action_spec(struct seq + } + } + +-static void print_onmax_spec(struct seq_file *m, +- struct hist_trigger_data *hist_data, +- struct action_data *data) +-{ +- seq_puts(m, ":onmax("); +- seq_printf(m, "%s", data->onmax.var_str); ++static void print_track_data_spec(struct seq_file *m, ++ struct hist_trigger_data *hist_data, ++ struct action_data *data) ++{ ++ if (data->handler == HANDLER_ONMAX) ++ seq_puts(m, ":onmax("); ++ seq_printf(m, "%s", data->track_data.var_str); + seq_printf(m, ").%s(", data->action_name); + + print_action_spec(m, hist_data, data); +@@ -4634,8 +4718,8 @@ static bool actions_match(struct hist_tr + data_test->match_data.event) != 0) + return false; + } else if (data->handler == HANDLER_ONMAX) { +- if (strcmp(data->onmax.var_str, +- data_test->onmax.var_str) != 0) ++ if (strcmp(data->track_data.var_str, ++ data_test->track_data.var_str) != 0) + return false; + } + } +@@ -4655,7 +4739,7 @@ static void print_actions_spec(struct se + if (data->handler == HANDLER_ONMATCH) + print_onmatch_spec(m, hist_data, data); + else if (data->handler == HANDLER_ONMAX) +- print_onmax_spec(m, hist_data, data); ++ print_track_data_spec(m, hist_data, data); + } + } + diff --git a/queue-4.19/tracing-have-the-historgram-use-the-result-of-str_has_prefix-for-len-of-prefix.patch b/queue-4.19/tracing-have-the-historgram-use-the-result-of-str_has_prefix-for-len-of-prefix.patch new file mode 100644 index 00000000000..f68a295e7aa --- /dev/null +++ b/queue-4.19/tracing-have-the-historgram-use-the-result-of-str_has_prefix-for-len-of-prefix.patch @@ -0,0 +1,53 @@ +From stable+bounces-43486-greg=kroah.com@vger.kernel.org Thu May 9 04:30:45 2024 +From: George Guo +Date: Thu, 9 May 2024 10:29:27 +0800 +Subject: tracing: Have the historgram use the result of str_has_prefix() for len of prefix +To: gregkh@linuxfoundation.org, rostedt@goodmis.org, mhiramat@kernel.org, tom.zanussi@linux.intel.com +Cc: stable@vger.kernel.org, Tom Zanussi , Namhyung Kim , George Guo +Message-ID: <20240509022931.3513365-10-dongtai.guo@linux.dev> + +From: "Steven Rostedt (VMware)" + +commit 036876fa56204ae0fa59045bd6bbb2691a060633 upstream. + +As str_has_prefix() returns the length on match, we can use that for the +updating of the string pointer instead of recalculating the prefix size. + +Cc: Tom Zanussi +Acked-by: Namhyung Kim +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: George Guo +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/trace_events_hist.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -4410,12 +4410,13 @@ static int parse_actions(struct hist_tri + unsigned int i; + int ret = 0; + char *str; ++ int len; + + for (i = 0; i < hist_data->attrs->n_actions; i++) { + str = hist_data->attrs->action_str[i]; + +- if (str_has_prefix(str, "onmatch(")) { +- char *action_str = str + sizeof("onmatch(") - 1; ++ if ((len = str_has_prefix(str, "onmatch("))) { ++ char *action_str = str + len; + + data = onmatch_parse(tr, action_str); + if (IS_ERR(data)) { +@@ -4423,8 +4424,8 @@ static int parse_actions(struct hist_tri + break; + } + data->fn = action_trace; +- } else if (str_has_prefix(str, "onmax(")) { +- char *action_str = str + sizeof("onmax(") - 1; ++ } else if ((len = str_has_prefix(str, "onmax("))) { ++ char *action_str = str + len; + + data = onmax_parse(action_str); + if (IS_ERR(data)) { diff --git a/queue-4.19/tracing-refactor-hist-trigger-action-code.patch b/queue-4.19/tracing-refactor-hist-trigger-action-code.patch new file mode 100644 index 00000000000..b2aae0149eb --- /dev/null +++ b/queue-4.19/tracing-refactor-hist-trigger-action-code.patch @@ -0,0 +1,851 @@ +From stable+bounces-43487-greg=kroah.com@vger.kernel.org Thu May 9 04:30:46 2024 +From: George Guo +Date: Thu, 9 May 2024 10:29:28 +0800 +Subject: tracing: Refactor hist trigger action code +To: gregkh@linuxfoundation.org, rostedt@goodmis.org, mhiramat@kernel.org, tom.zanussi@linux.intel.com +Cc: stable@vger.kernel.org, George Guo +Message-ID: <20240509022931.3513365-11-dongtai.guo@linux.dev> + +From: Tom Zanussi + +commit 7d18a10c316783357fb1b2b649cfcf97c70a7bee upstream. + +The hist trigger action code currently implements two essentially +hard-coded pairs of 'actions' - onmax(), which tracks a variable and +saves some event fields when a max is hit, and onmatch(), which is +hard-coded to generate a synthetic event. + +These hardcoded pairs (track max/save fields and detect match/generate +synthetic event) should really be decoupled into separate components +that can then be arbitrarily combined. The first component of each +pair (track max/detect match) is called a 'handler' in the new code, +while the second component (save fields/generate synthetic event) is +called an 'action' in this scheme. + +This change refactors the action code to reflect this split by adding +two handlers, HANDLER_ONMATCH and HANDLER_ONMAX, along with two +actions, ACTION_SAVE and ACTION_TRACE. + +The new code combines them to produce the existing ONMATCH/TRACE and +ONMAX/SAVE functionality, but doesn't implement the other combinations +now possible. Future patches will expand these to further useful +cases, such as ONMAX/TRACE, as well as add additional handlers and +actions such as ONCHANGE and SNAPSHOT. + +Also, add abbreviated documentation for handlers and actions to +README. + +Link: http://lkml.kernel.org/r/98bfdd48c1b4ff29fc5766442f99f5bc3c34b76b.1550100284.git.tom.zanussi@linux.intel.com + +Signed-off-by: Tom Zanussi +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: George Guo +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/trace_events_hist.c | 407 ++++++++++++++++++++++----------------- + 1 file changed, 238 insertions(+), 169 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -287,9 +287,9 @@ struct hist_trigger_data { + 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 field_var *save_vars[SYNTH_FIELDS_MAX]; ++ unsigned int n_save_vars; ++ unsigned int n_save_var_str; + }; + + static int synth_event_create(int argc, const char **argv); +@@ -357,11 +357,25 @@ 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 ring_buffer_event *rbe, void *key, + struct action_data *data, u64 *var_ref_vals); + ++enum handler_id { ++ HANDLER_ONMATCH = 1, ++ HANDLER_ONMAX, ++}; ++ ++enum action_id { ++ ACTION_SAVE = 1, ++ ACTION_TRACE, ++}; ++ + struct action_data { ++ enum handler_id handler; ++ enum action_id action; ++ char *action_name; + action_fn_t fn; ++ + unsigned int n_params; + char *params[SYNTH_FIELDS_MAX]; + +@@ -370,13 +384,11 @@ struct action_data { + unsigned int var_ref_idx; + char *match_event; + char *match_event_system; +- char *synth_event_name; + struct synth_event *synth_event; + } onmatch; + + struct { + char *var_str; +- char *fn_name; + unsigned int max_var_ref_idx; + struct hist_field *max_var; + struct hist_field *var; +@@ -1065,7 +1077,7 @@ static struct synth_event *alloc_synth_e + + static void action_trace(struct hist_trigger_data *hist_data, + struct tracing_map_elt *elt, void *rec, +- struct ring_buffer_event *rbe, ++ struct ring_buffer_event *rbe, void *key, + struct action_data *data, u64 *var_ref_vals) + { + struct synth_event *event = data->onmatch.synth_event; +@@ -1635,7 +1647,7 @@ find_match_var(struct hist_trigger_data + for (i = 0; i < hist_data->n_actions; i++) { + struct action_data *data = hist_data->actions[i]; + +- if (data->fn == action_trace) { ++ if (data->handler == HANDLER_ONMATCH) { + char *system = data->onmatch.match_event_system; + char *event_name = data->onmatch.match_event; + +@@ -2073,7 +2085,7 @@ static int hist_trigger_elt_data_alloc(s + } + } + +- n_str = hist_data->n_field_var_str + hist_data->n_max_var_str; ++ n_str = hist_data->n_field_var_str + hist_data->n_save_var_str; + + size = STR_VAR_LEN_MAX; + +@@ -3115,7 +3127,7 @@ create_field_var_hist(struct hist_trigge + int ret; + + if (target_hist_data->n_field_var_hists >= SYNTH_FIELDS_MAX) { +- hist_err_event("onmatch: Too many field variables defined: ", ++ hist_err_event("trace action: Too many field variables defined: ", + subsys_name, event_name, field_name); + return ERR_PTR(-EINVAL); + } +@@ -3123,7 +3135,7 @@ create_field_var_hist(struct hist_trigge + file = event_file(tr, subsys_name, event_name); + + if (IS_ERR(file)) { +- hist_err_event("onmatch: Event file not found: ", ++ hist_err_event("trace action: Event file not found: ", + subsys_name, event_name, field_name); + ret = PTR_ERR(file); + return ERR_PTR(ret); +@@ -3137,7 +3149,7 @@ create_field_var_hist(struct hist_trigge + */ + hist_data = find_compatible_hist(target_hist_data, file); + if (!hist_data) { +- hist_err_event("onmatch: Matching event histogram not found: ", ++ hist_err_event("trace action: Matching event histogram not found: ", + subsys_name, event_name, field_name); + return ERR_PTR(-EINVAL); + } +@@ -3199,7 +3211,7 @@ 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: ", ++ hist_err_event("trace action: Couldn't create histogram for field: ", + subsys_name, event_name, field_name); + return ERR_PTR(ret); + } +@@ -3212,7 +3224,7 @@ create_field_var_hist(struct hist_trigge + if (IS_ERR_OR_NULL(event_var)) { + kfree(var_hist->cmd); + kfree(var_hist); +- hist_err_event("onmatch: Couldn't find synthetic variable: ", ++ hist_err_event("trace action: Couldn't find synthetic variable: ", + subsys_name, event_name, field_name); + return ERR_PTR(-EINVAL); + } +@@ -3295,8 +3307,8 @@ static void update_max_vars(struct hist_ + 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); ++ __update_field_vars(elt, rbe, rec, hist_data->save_vars, ++ hist_data->n_save_vars, hist_data->n_field_var_str); + } + + static struct hist_field *create_var(struct hist_trigger_data *hist_data, +@@ -3440,9 +3452,9 @@ static void onmax_print(struct seq_file + + 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; ++ for (i = 0; i < hist_data->n_save_vars; i++) { ++ struct hist_field *save_val = hist_data->save_vars[i]->val; ++ struct hist_field *save_var = hist_data->save_vars[i]->var; + u64 val; + + save_var_idx = save_var->var.idx; +@@ -3459,7 +3471,7 @@ static void onmax_print(struct seq_file + + static void onmax_save(struct hist_trigger_data *hist_data, + struct tracing_map_elt *elt, void *rec, +- struct ring_buffer_event *rbe, ++ struct ring_buffer_event *rbe, void *key, + struct action_data *data, u64 *var_ref_vals) + { + unsigned int max_idx = data->onmax.max_var->var.idx; +@@ -3486,7 +3498,7 @@ static void onmax_destroy(struct action_ + destroy_hist_field(data->onmax.var, 0); + + kfree(data->onmax.var_str); +- kfree(data->onmax.fn_name); ++ kfree(data->action_name); + + for (i = 0; i < data->n_params; i++) + kfree(data->params[i]); +@@ -3494,15 +3506,16 @@ static void onmax_destroy(struct action_ + kfree(data); + } + ++static int action_create(struct hist_trigger_data *hist_data, ++ struct action_data *data); ++ + static int onmax_create(struct hist_trigger_data *hist_data, + struct action_data *data) + { ++ struct hist_field *var_field, *ref_field, *max_var = NULL; + 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; +- unsigned int i; ++ char *onmax_var_str; + int ret = 0; + + onmax_var_str = data->onmax.var_str; +@@ -3524,8 +3537,8 @@ static int onmax_create(struct hist_trig + + data->onmax.var = ref_field; + +- data->fn = onmax_save; + data->onmax.max_var_ref_idx = var_ref_idx; ++ + max_var = create_var(hist_data, file, "max", sizeof(u64), "u64"); + if (IS_ERR(max_var)) { + hist_err("onmax: Couldn't create onmax variable: ", "max"); +@@ -3534,27 +3547,7 @@ static int onmax_create(struct hist_trig + } + data->onmax.max_var = max_var; + +- for (i = 0; i < data->n_params; i++) { +- param = kstrdup(data->params[i], GFP_KERNEL); +- if (!param) { +- ret = -ENOMEM; +- goto out; +- } +- +- 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; +- } +- +- 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); +- } ++ ret = action_create(hist_data, data); + out: + return ret; + } +@@ -3565,11 +3558,14 @@ static int parse_action_params(char *par + int ret = 0; + + while (params) { +- if (data->n_params >= SYNTH_FIELDS_MAX) ++ if (data->n_params >= SYNTH_FIELDS_MAX) { ++ hist_err("Too many action params", ""); + goto out; ++ } + + param = strsep(¶ms, ","); + if (!param) { ++ hist_err("No action param found", ""); + ret = -EINVAL; + goto out; + } +@@ -3593,10 +3589,71 @@ static int parse_action_params(char *par + return ret; + } + +-static struct action_data *onmax_parse(char *str) ++static int action_parse(char *str, struct action_data *data, ++ enum handler_id handler) ++{ ++ char *action_name; ++ int ret = 0; ++ ++ strsep(&str, "."); ++ if (!str) { ++ hist_err("action parsing: No action found", ""); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ action_name = strsep(&str, "("); ++ if (!action_name || !str) { ++ hist_err("action parsing: No action found", ""); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (str_has_prefix(action_name, "save")) { ++ char *params = strsep(&str, ")"); ++ ++ if (!params) { ++ hist_err("action parsing: No params found for %s", "save"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ ret = parse_action_params(params, data); ++ if (ret) ++ goto out; ++ ++ if (handler == HANDLER_ONMAX) ++ data->fn = onmax_save; ++ ++ data->action = ACTION_SAVE; ++ } else { ++ char *params = strsep(&str, ")"); ++ ++ if (params) { ++ ret = parse_action_params(params, data); ++ if (ret) ++ goto out; ++ } ++ ++ data->fn = action_trace; ++ data->action = ACTION_TRACE; ++ } ++ ++ data->action_name = kstrdup(action_name, GFP_KERNEL); ++ if (!data->action_name) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ data->handler = handler; ++ out: ++ return ret; ++} ++ ++static struct action_data *onmax_parse(char *str, enum handler_id handler) + { +- char *onmax_fn_name, *onmax_var_str; + struct action_data *data; ++ char *onmax_var_str; + int ret = -EINVAL; + + data = kzalloc(sizeof(*data), GFP_KERNEL); +@@ -3615,33 +3672,9 @@ static struct action_data *onmax_parse(c + goto free; + } + +- strsep(&str, "."); +- if (!str) +- goto free; +- +- onmax_fn_name = strsep(&str, "("); +- if (!onmax_fn_name || !str) +- goto free; +- +- if (str_has_prefix(onmax_fn_name, "save")) { +- char *params = strsep(&str, ")"); +- +- if (!params) { +- ret = -EINVAL; +- goto free; +- } +- +- ret = parse_action_params(params, data); +- if (ret) +- goto free; +- } else +- goto free; +- +- data->onmax.fn_name = kstrdup(onmax_fn_name, GFP_KERNEL); +- if (!data->onmax.fn_name) { +- ret = -ENOMEM; ++ ret = action_parse(str, data, handler); ++ if (ret) + goto free; +- } + out: + return data; + free: +@@ -3658,7 +3691,7 @@ static void onmatch_destroy(struct actio + + kfree(data->onmatch.match_event); + kfree(data->onmatch.match_event_system); +- kfree(data->onmatch.synth_event_name); ++ kfree(data->action_name); + + for (i = 0; i < data->n_params; i++) + kfree(data->params[i]); +@@ -3716,8 +3749,9 @@ static int check_synth_field(struct synt + } + + static struct hist_field * +-onmatch_find_var(struct hist_trigger_data *hist_data, struct action_data *data, +- char *system, char *event, char *var) ++trace_action_find_var(struct hist_trigger_data *hist_data, ++ struct action_data *data, ++ char *system, char *event, char *var) + { + struct hist_field *hist_field; + +@@ -3725,7 +3759,7 @@ onmatch_find_var(struct hist_trigger_dat + + hist_field = find_target_event_var(hist_data, system, event, var); + if (!hist_field) { +- if (!system) { ++ if (!system && data->handler == HANDLER_ONMATCH) { + system = data->onmatch.match_event_system; + event = data->onmatch.match_event; + } +@@ -3734,15 +3768,15 @@ onmatch_find_var(struct hist_trigger_dat + } + + if (!hist_field) +- hist_err_event("onmatch: Couldn't find onmatch param: $", system, event, var); ++ hist_err_event("trace action: Couldn't find param: $", 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) ++trace_action_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; +@@ -3765,7 +3799,7 @@ onmatch_create_field_var(struct hist_tri + * looking for fields on the onmatch(system.event.xxx) + * event. + */ +- if (!system) { ++ if (!system && data->handler == HANDLER_ONMATCH) { + system = data->onmatch.match_event_system; + event = data->onmatch.match_event; + } +@@ -3791,9 +3825,8 @@ onmatch_create_field_var(struct hist_tri + goto out; + } + +-static int onmatch_create(struct hist_trigger_data *hist_data, +- struct trace_event_file *file, +- struct action_data *data) ++static int trace_action_create(struct hist_trigger_data *hist_data, ++ struct action_data *data) + { + char *event_name, *param, *system = NULL; + struct hist_field *hist_field, *var_ref; +@@ -3804,11 +3837,12 @@ static int onmatch_create(struct hist_tr + + lockdep_assert_held(&event_mutex); + +- event = find_synth_event(data->onmatch.synth_event_name); ++ event = find_synth_event(data->action_name); + if (!event) { +- hist_err("onmatch: Couldn't find synthetic event: ", data->onmatch.synth_event_name); ++ hist_err("trace action: Couldn't find synthetic event: ", data->action_name); + return -EINVAL; + } ++ + event->ref++; + + var_ref_idx = hist_data->n_var_refs; +@@ -3836,13 +3870,15 @@ static int onmatch_create(struct hist_tr + } + + if (param[0] == '$') +- hist_field = onmatch_find_var(hist_data, data, system, +- event_name, param); ++ hist_field = trace_action_find_var(hist_data, data, ++ system, event_name, ++ param); + else +- hist_field = onmatch_create_field_var(hist_data, data, +- system, +- event_name, +- param); ++ hist_field = trace_action_create_field_var(hist_data, ++ data, ++ system, ++ event_name, ++ param); + + if (!hist_field) { + kfree(p); +@@ -3864,7 +3900,7 @@ static int onmatch_create(struct hist_tr + continue; + } + +- hist_err_event("onmatch: Param type doesn't match synthetic event field type: ", ++ hist_err_event("trace action: Param type doesn't match synthetic event field type: ", + system, event_name, param); + kfree(p); + ret = -EINVAL; +@@ -3872,12 +3908,11 @@ static int onmatch_create(struct hist_tr + } + + if (field_pos != event->n_fields) { +- hist_err("onmatch: Param count doesn't match synthetic event field count: ", event->name); ++ hist_err("trace action: Param count doesn't match synthetic event field count: ", event->name); + ret = -EINVAL; + goto err; + } + +- data->fn = action_trace; + data->onmatch.synth_event = event; + data->onmatch.var_ref_idx = var_ref_idx; + out: +@@ -3888,10 +3923,58 @@ static int onmatch_create(struct hist_tr + goto out; + } + ++static int action_create(struct hist_trigger_data *hist_data, ++ struct action_data *data) ++{ ++ struct field_var *field_var; ++ unsigned int i; ++ char *param; ++ int ret = 0; ++ ++ if (data->action == ACTION_TRACE) ++ return trace_action_create(hist_data, data); ++ ++ if (data->action == ACTION_SAVE) { ++ if (hist_data->n_save_vars) { ++ ret = -EEXIST; ++ hist_err("save action: Can't have more than one save() action per hist", ""); ++ goto out; ++ } ++ ++ for (i = 0; i < data->n_params; i++) { ++ param = kstrdup(data->params[i], GFP_KERNEL); ++ if (!param) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ field_var = create_target_field_var(hist_data, NULL, NULL, param); ++ if (IS_ERR(field_var)) { ++ hist_err("save action: Couldn't create field variable: ", param); ++ ret = PTR_ERR(field_var); ++ kfree(param); ++ goto out; ++ } ++ ++ hist_data->save_vars[hist_data->n_save_vars++] = field_var; ++ if (field_var->val->flags & HIST_FIELD_FL_STRING) ++ hist_data->n_save_var_str++; ++ kfree(param); ++ } ++ } ++ out: ++ return ret; ++} ++ ++static int onmatch_create(struct hist_trigger_data *hist_data, ++ struct action_data *data) ++{ ++ return action_create(hist_data, data); ++} ++ + static struct action_data *onmatch_parse(struct trace_array *tr, char *str) + { + char *match_event, *match_event_system; +- char *synth_event_name, *params; + struct action_data *data; + int ret = -EINVAL; + +@@ -3929,31 +4012,7 @@ static struct action_data *onmatch_parse + goto free; + } + +- strsep(&str, "."); +- if (!str) { +- hist_err("onmatch: Missing . after onmatch(): ", str); +- goto free; +- } +- +- synth_event_name = strsep(&str, "("); +- if (!synth_event_name || !str) { +- hist_err("onmatch: Missing opening paramlist paren: ", synth_event_name); +- goto free; +- } +- +- data->onmatch.synth_event_name = kstrdup(synth_event_name, GFP_KERNEL); +- if (!data->onmatch.synth_event_name) { +- ret = -ENOMEM; +- goto free; +- } +- +- params = strsep(&str, ")"); +- if (!params || !str || (str && strlen(str))) { +- hist_err("onmatch: Missing closing paramlist paren: ", params); +- goto free; +- } +- +- ret = parse_action_params(params, data); ++ ret = action_parse(str, data, HANDLER_ONMATCH); + if (ret) + goto free; + out: +@@ -4394,9 +4453,9 @@ static void destroy_actions(struct hist_ + for (i = 0; i < hist_data->n_actions; i++) { + struct action_data *data = hist_data->actions[i]; + +- if (data->fn == action_trace) ++ if (data->handler == HANDLER_ONMATCH) + onmatch_destroy(data); +- else if (data->fn == onmax_save) ++ else if (data->handler == HANDLER_ONMAX) + onmax_destroy(data); + else + kfree(data); +@@ -4423,16 +4482,14 @@ static int parse_actions(struct hist_tri + ret = PTR_ERR(data); + break; + } +- data->fn = action_trace; + } else if ((len = str_has_prefix(str, "onmax("))) { + char *action_str = str + len; + +- data = onmax_parse(action_str); ++ data = onmax_parse(action_str, HANDLER_ONMAX); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + break; + } +- data->fn = onmax_save; + } else { + ret = -EINVAL; + break; +@@ -4444,8 +4501,7 @@ static int parse_actions(struct hist_tri + return ret; + } + +-static int create_actions(struct hist_trigger_data *hist_data, +- struct trace_event_file *file) ++static int create_actions(struct hist_trigger_data *hist_data) + { + struct action_data *data; + unsigned int i; +@@ -4454,14 +4510,17 @@ static int create_actions(struct hist_tr + for (i = 0; i < hist_data->attrs->n_actions; i++) { + data = hist_data->actions[i]; + +- if (data->fn == action_trace) { +- ret = onmatch_create(hist_data, file, data); ++ if (data->handler == HANDLER_ONMATCH) { ++ ret = onmatch_create(hist_data, data); + if (ret) +- return ret; +- } else if (data->fn == onmax_save) { ++ break; ++ } else if (data->handler == HANDLER_ONMAX) { + ret = onmax_create(hist_data, data); + if (ret) +- return ret; ++ break; ++ } else { ++ ret = -EINVAL; ++ break; + } + } + +@@ -4477,26 +4536,42 @@ 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) ++ if (data->handler == HANDLER_ONMAX) + onmax_print(m, hist_data, elt, data); + } + } + ++static void print_action_spec(struct seq_file *m, ++ struct hist_trigger_data *hist_data, ++ struct action_data *data) ++{ ++ unsigned int i; ++ ++ if (data->action == ACTION_SAVE) { ++ for (i = 0; i < hist_data->n_save_vars; i++) { ++ seq_printf(m, "%s", hist_data->save_vars[i]->var->var.name); ++ if (i < hist_data->n_save_vars - 1) ++ seq_puts(m, ","); ++ } ++ } else if (data->action == ACTION_TRACE) { ++ for (i = 0; i < data->n_params; i++) { ++ if (i) ++ seq_puts(m, ","); ++ seq_printf(m, "%s", data->params[i]); ++ } ++ } ++} ++ + 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); ++ seq_printf(m, ").%s(", data->action_name); ++ ++ print_action_spec(m, hist_data, data); + +- 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, ")"); + } + +@@ -4504,18 +4579,12 @@ static void print_onmatch_spec(struct se + struct hist_trigger_data *hist_data, + struct action_data *data) + { +- unsigned int i; +- + seq_printf(m, ":onmatch(%s.%s).", data->onmatch.match_event_system, + data->onmatch.match_event); + +- seq_printf(m, "%s(", data->onmatch.synth_event->name); ++ seq_printf(m, "%s(", data->action_name); + +- for (i = 0; i < data->n_params; i++) { +- if (i) +- seq_puts(m, ","); +- seq_printf(m, "%s", data->params[i]); +- } ++ print_action_spec(m, hist_data, data); + + seq_puts(m, ")"); + } +@@ -4532,7 +4601,9 @@ static bool actions_match(struct hist_tr + struct action_data *data = hist_data->actions[i]; + struct action_data *data_test = hist_data_test->actions[i]; + +- if (data->fn != data_test->fn) ++ if (data->handler != data_test->handler) ++ return false; ++ if (data->action != data_test->action) + return false; + + if (data->n_params != data_test->n_params) +@@ -4543,23 +4614,20 @@ static bool actions_match(struct hist_tr + return false; + } + +- if (data->fn == action_trace) { +- if (strcmp(data->onmatch.synth_event_name, +- data_test->onmatch.synth_event_name) != 0) +- return false; ++ if (strcmp(data->action_name, data_test->action_name) != 0) ++ return false; ++ ++ if (data->handler == HANDLER_ONMATCH) { + if (strcmp(data->onmatch.match_event_system, + data_test->onmatch.match_event_system) != 0) + return false; + if (strcmp(data->onmatch.match_event, + data_test->onmatch.match_event) != 0) + return false; +- } else if (data->fn == onmax_save) { ++ } else if (data->handler == HANDLER_ONMAX) { + if (strcmp(data->onmax.var_str, + data_test->onmax.var_str) != 0) + return false; +- if (strcmp(data->onmax.fn_name, +- data_test->onmax.fn_name) != 0) +- return false; + } + } + +@@ -4575,9 +4643,9 @@ static void print_actions_spec(struct se + for (i = 0; i < hist_data->n_actions; i++) { + struct action_data *data = hist_data->actions[i]; + +- if (data->fn == action_trace) ++ if (data->handler == HANDLER_ONMATCH) + print_onmatch_spec(m, hist_data, data); +- else if (data->fn == onmax_save) ++ else if (data->handler == HANDLER_ONMAX) + print_onmax_spec(m, hist_data, data); + } + } +@@ -4770,14 +4838,15 @@ static inline void add_to_key(char *comp + 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 ring_buffer_event *rbe, void *key, ++ 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); ++ data->fn(hist_data, elt, rec, rbe, key, data, var_ref_vals); + } + } + +@@ -4838,7 +4907,7 @@ static void event_hist_trigger(struct ev + 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); ++ hist_trigger_actions(hist_data, elt, rec, rbe, key, var_ref_vals); + } + + static void hist_trigger_stacktrace_print(struct seq_file *m, +@@ -5757,7 +5826,7 @@ static int event_hist_trigger_func(struc + if (get_named_trigger_data(trigger_data)) + goto enable; + +- ret = create_actions(hist_data, file); ++ ret = create_actions(hist_data); + if (ret) + goto out_unreg; + diff --git a/queue-4.19/tracing-remove-unnecessary-var_ref-destroy-in-track_data_destroy.patch b/queue-4.19/tracing-remove-unnecessary-var_ref-destroy-in-track_data_destroy.patch new file mode 100644 index 00000000000..b8e61a876af --- /dev/null +++ b/queue-4.19/tracing-remove-unnecessary-var_ref-destroy-in-track_data_destroy.patch @@ -0,0 +1,107 @@ +From stable+bounces-43490-greg=kroah.com@vger.kernel.org Thu May 9 04:30:50 2024 +From: George Guo +Date: Thu, 9 May 2024 10:29:31 +0800 +Subject: tracing: Remove unnecessary var_ref destroy in track_data_destroy() +To: gregkh@linuxfoundation.org, rostedt@goodmis.org, mhiramat@kernel.org, tom.zanussi@linux.intel.com +Cc: stable@vger.kernel.org, George Guo +Message-ID: <20240509022931.3513365-14-dongtai.guo@linux.dev> + +From: Tom Zanussi + +commit ff9d31d0d46672e201fc9ff59c42f1eef5f00c77 upstream. + +Commit 656fe2ba85e8 (tracing: Use hist trigger's var_ref array to +destroy var_refs) centralized the destruction of all the var_refs +in one place so that other code didn't have to do it. + +The track_data_destroy() added later ignored that and also destroyed +the track_data var_ref, causing a double-free error flagged by KASAN. + +================================================================== +BUG: KASAN: use-after-free in destroy_hist_field+0x30/0x70 +Read of size 8 at addr ffff888086df2210 by task bash/1694 + +CPU: 6 PID: 1694 Comm: bash Not tainted 5.1.0-rc1-test+ #15 +Hardware name: Hewlett-Packard HP Compaq Pro 6300 SFF/339A, BIOS K01 v03.03 +07/14/2016 +Call Trace: + dump_stack+0x71/0xa0 + ? destroy_hist_field+0x30/0x70 + print_address_description.cold.3+0x9/0x1fb + ? destroy_hist_field+0x30/0x70 + ? destroy_hist_field+0x30/0x70 + kasan_report.cold.4+0x1a/0x33 + ? __kasan_slab_free+0x100/0x150 + ? destroy_hist_field+0x30/0x70 + destroy_hist_field+0x30/0x70 + track_data_destroy+0x55/0xe0 + destroy_hist_data+0x1f0/0x350 + hist_unreg_all+0x203/0x220 + event_trigger_open+0xbb/0x130 + do_dentry_open+0x296/0x700 + ? stacktrace_count_trigger+0x30/0x30 + ? generic_permission+0x56/0x200 + ? __x64_sys_fchdir+0xd0/0xd0 + ? inode_permission+0x55/0x200 + ? security_inode_permission+0x18/0x60 + path_openat+0x633/0x22b0 + ? path_lookupat.isra.50+0x420/0x420 + ? __kasan_kmalloc.constprop.12+0xc1/0xd0 + ? kmem_cache_alloc+0xe5/0x260 + ? getname_flags+0x6c/0x2a0 + ? do_sys_open+0x149/0x2b0 + ? do_syscall_64+0x73/0x1b0 + ? entry_SYSCALL_64_after_hwframe+0x44/0xa9 + ? _raw_write_lock_bh+0xe0/0xe0 + ? __kernel_text_address+0xe/0x30 + ? unwind_get_return_address+0x2f/0x50 + ? __list_add_valid+0x2d/0x70 + ? deactivate_slab.isra.62+0x1f4/0x5a0 + ? getname_flags+0x6c/0x2a0 + ? set_track+0x76/0x120 + do_filp_open+0x11a/0x1a0 + ? may_open_dev+0x50/0x50 + ? _raw_spin_lock+0x7a/0xd0 + ? _raw_write_lock_bh+0xe0/0xe0 + ? __alloc_fd+0x10f/0x200 + do_sys_open+0x1db/0x2b0 + ? filp_open+0x50/0x50 + do_syscall_64+0x73/0x1b0 + entry_SYSCALL_64_after_hwframe+0x44/0xa9 +RIP: 0033:0x7fa7b24a4ca2 +Code: 25 00 00 41 00 3d 00 00 41 00 74 4c 48 8d 05 85 7a 0d 00 8b 00 85 c0 +75 6d 89 f2 b8 01 01 00 00 48 89 fe bf 9c ff ff ff 0f 05 <48> 3d 00 f0 ff ff +0f 87 a2 00 00 00 48 8b 4c 24 28 64 48 33 0c 25 +RSP: 002b:00007fffbafb3af0 EFLAGS: 00000246 ORIG_RAX: 0000000000000101 +RAX: ffffffffffffffda RBX: 000055d3648ade30 RCX: 00007fa7b24a4ca2 +RDX: 0000000000000241 RSI: 000055d364a55240 RDI: 00000000ffffff9c +RBP: 00007fffbafb3bf0 R08: 0000000000000020 R09: 0000000000000002 +R10: 00000000000001b6 R11: 0000000000000246 R12: 0000000000000000 +R13: 0000000000000003 R14: 0000000000000001 R15: 000055d364a55240 +================================================================== + +So remove the track_data_destroy() destroy_hist_field() call for that +var_ref. + +Link: http://lkml.kernel.org/r/1deffec420f6a16d11dd8647318d34a66d1989a9.camel@linux.intel.com + +Fixes: 466f4528fbc69 ("tracing: Generalize hist trigger onmax and save action") +Reported-by: Steven Rostedt (VMware) +Signed-off-by: Tom Zanussi +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: George Guo +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/trace_events_hist.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -3587,7 +3587,6 @@ static void track_data_destroy(struct hi + struct action_data *data) + { + destroy_hist_field(data->track_data.track_var, 0); +- destroy_hist_field(data->track_data.var_ref, 0); + + kfree(data->track_data.var_str); + diff --git a/queue-4.19/tracing-remove-unneeded-synth_event_mutex.patch b/queue-4.19/tracing-remove-unneeded-synth_event_mutex.patch new file mode 100644 index 00000000000..e3d3f2812d5 --- /dev/null +++ b/queue-4.19/tracing-remove-unneeded-synth_event_mutex.patch @@ -0,0 +1,195 @@ +From stable+bounces-43481-greg=kroah.com@vger.kernel.org Thu May 9 04:30:22 2024 +From: George Guo +Date: Thu, 9 May 2024 10:29:22 +0800 +Subject: tracing: Remove unneeded synth_event_mutex +To: gregkh@linuxfoundation.org, rostedt@goodmis.org, mhiramat@kernel.org, tom.zanussi@linux.intel.com +Cc: stable@vger.kernel.org, George Guo +Message-ID: <20240509022931.3513365-5-dongtai.guo@linux.dev> + +From: Masami Hiramatsu + +commit 0e2b81f7b52a1c1a8c46986f9ca01eb7b3c421f8 upstream. + +Rmove unneeded synth_event_mutex. This mutex protects the reference +count in synth_event, however, those operational points are already +protected by event_mutex. + +1. In __create_synth_event() and create_or_delete_synth_event(), + those synth_event_mutex clearly obtained right after event_mutex. + +2. event_hist_trigger_func() is trigger_hist_cmd.func() which is + called by trigger_process_regex(), which is a part of + event_trigger_regex_write() and this function takes event_mutex. + +3. hist_unreg_all() is trigger_hist_cmd.unreg_all() which is called + by event_trigger_regex_open() and it takes event_mutex. + +4. onmatch_destroy() and onmatch_create() have long call tree, + but both are finally invoked from event_trigger_regex_write() + and event_trace_del_tracer(), former takes event_mutex, and latter + ensures called under event_mutex locked. + +Finally, I ensured there is no resource conflict. For safety, +I added lockdep_assert_held(&event_mutex) for each function. + +Link: http://lkml.kernel.org/r/154140864134.17322.4796059721306031894.stgit@devbox + +Reviewed-by: Tom Zanussi +Tested-by: Tom Zanussi +Signed-off-by: Masami Hiramatsu +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: George Guo +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/trace_events_hist.c | 30 +++++++----------------------- + 1 file changed, 7 insertions(+), 23 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -443,8 +443,6 @@ static bool have_hist_err(void) + return false; + } + +-static DEFINE_MUTEX(synth_event_mutex); +- + struct synth_trace_event { + struct trace_entry ent; + u64 fields[]; +@@ -1097,7 +1095,6 @@ static int __create_synth_event(int argc + return -EINVAL; + + mutex_lock(&event_mutex); +- mutex_lock(&synth_event_mutex); + + event = find_synth_event(name); + if (event) { +@@ -1139,7 +1136,6 @@ static int __create_synth_event(int argc + else + free_synth_event(event); + out: +- mutex_unlock(&synth_event_mutex); + mutex_unlock(&event_mutex); + + return ret; +@@ -1159,7 +1155,6 @@ static int create_or_delete_synth_event( + /* trace_run_command() ensures argc != 0 */ + if (name[0] == '!') { + mutex_lock(&event_mutex); +- mutex_lock(&synth_event_mutex); + event = find_synth_event(name + 1); + if (event) { + if (event->ref) +@@ -1173,7 +1168,6 @@ static int create_or_delete_synth_event( + } + } else + ret = -ENOENT; +- mutex_unlock(&synth_event_mutex); + mutex_unlock(&event_mutex); + return ret; + } +@@ -3660,7 +3654,7 @@ static void onmatch_destroy(struct actio + { + unsigned int i; + +- mutex_lock(&synth_event_mutex); ++ lockdep_assert_held(&event_mutex); + + kfree(data->onmatch.match_event); + kfree(data->onmatch.match_event_system); +@@ -3673,8 +3667,6 @@ static void onmatch_destroy(struct actio + data->onmatch.synth_event->ref--; + + kfree(data); +- +- mutex_unlock(&synth_event_mutex); + } + + static void destroy_field_var(struct field_var *field_var) +@@ -3810,15 +3802,14 @@ static int onmatch_create(struct hist_tr + struct synth_event *event; + int ret = 0; + +- mutex_lock(&synth_event_mutex); ++ lockdep_assert_held(&event_mutex); ++ + event = find_synth_event(data->onmatch.synth_event_name); + if (!event) { + hist_err("onmatch: Couldn't find synthetic event: ", data->onmatch.synth_event_name); +- mutex_unlock(&synth_event_mutex); + return -EINVAL; + } + event->ref++; +- mutex_unlock(&synth_event_mutex); + + var_ref_idx = hist_data->n_var_refs; + +@@ -3892,9 +3883,7 @@ static int onmatch_create(struct hist_tr + out: + return ret; + err: +- mutex_lock(&synth_event_mutex); + event->ref--; +- mutex_unlock(&synth_event_mutex); + + goto out; + } +@@ -5611,6 +5600,8 @@ static void hist_unreg_all(struct trace_ + struct synth_event *se; + const char *se_name; + ++ lockdep_assert_held(&event_mutex); ++ + if (hist_file_check_refs(file)) + return; + +@@ -5620,12 +5611,10 @@ static void hist_unreg_all(struct trace_ + list_del_rcu(&test->list); + trace_event_trigger_enable_disable(file, 0); + +- mutex_lock(&synth_event_mutex); + se_name = trace_event_name(file->event_call); + se = find_synth_event(se_name); + if (se) + se->ref--; +- mutex_unlock(&synth_event_mutex); + + update_cond_flag(file); + if (hist_data->enable_timestamps) +@@ -5651,6 +5640,8 @@ static int event_hist_trigger_func(struc + char *trigger, *p; + int ret = 0; + ++ lockdep_assert_held(&event_mutex); ++ + if (glob && strlen(glob)) { + last_cmd_set(param); + hist_err_clear(); +@@ -5741,14 +5732,10 @@ static int event_hist_trigger_func(struc + } + + cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); +- +- mutex_lock(&synth_event_mutex); + se_name = trace_event_name(file->event_call); + se = find_synth_event(se_name); + if (se) + se->ref--; +- mutex_unlock(&synth_event_mutex); +- + ret = 0; + goto out_free; + } +@@ -5787,13 +5774,10 @@ enable: + if (ret) + goto out_unreg; + +- mutex_lock(&synth_event_mutex); + se_name = trace_event_name(file->event_call); + se = find_synth_event(se_name); + if (se) + se->ref++; +- mutex_unlock(&synth_event_mutex); +- + /* Just return zero, not the number of registered triggers */ + ret = 0; + out: diff --git a/queue-4.19/tracing-simplify-creation-and-deletion-of-synthetic-events.patch b/queue-4.19/tracing-simplify-creation-and-deletion-of-synthetic-events.patch new file mode 100644 index 00000000000..c9dccdccb02 --- /dev/null +++ b/queue-4.19/tracing-simplify-creation-and-deletion-of-synthetic-events.patch @@ -0,0 +1,128 @@ +From stable+bounces-43478-greg=kroah.com@vger.kernel.org Thu May 9 04:30:13 2024 +From: George Guo +Date: Thu, 9 May 2024 10:29:19 +0800 +Subject: tracing: Simplify creation and deletion of synthetic events +To: gregkh@linuxfoundation.org, rostedt@goodmis.org, mhiramat@kernel.org, tom.zanussi@linux.intel.com +Cc: stable@vger.kernel.org, George Guo +Message-ID: <20240509022931.3513365-2-dongtai.guo@linux.dev> + +From: Masami Hiramatsu + +commit faacb361f271be4baf2d807e2eeaba87e059225f upstream. + +Since the event_mutex and synth_event_mutex ordering issue +is gone, we can skip existing event check when adding or +deleting events, and some redundant code in error path. + +This changes release_all_synth_events() to abort the process +when it hits any error and returns the error code. It succeeds +only if it has no error. + +Link: http://lkml.kernel.org/r/154140847194.17322.17960275728005067803.stgit@devbox + +Reviewed-by: Tom Zanussi +Tested-by: Tom Zanussi +Signed-off-by: Masami Hiramatsu +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: George Guo +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/trace_events_hist.c | 53 +++++++++++++-------------------------- + 1 file changed, 18 insertions(+), 35 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -1028,18 +1028,6 @@ struct hist_var_data { + struct hist_trigger_data *hist_data; + }; + +-static void add_or_delete_synth_event(struct synth_event *event, int delete) +-{ +- if (delete) +- free_synth_event(event); +- else { +- if (!find_synth_event(event->name)) +- list_add(&event->list, &synth_event_list); +- else +- free_synth_event(event); +- } +-} +- + static int create_synth_event(int argc, char **argv) + { + struct synth_field *field, *fields[SYNTH_FIELDS_MAX]; +@@ -1072,15 +1060,16 @@ static int create_synth_event(int argc, + if (event) { + if (delete_event) { + if (event->ref) { +- event = NULL; + ret = -EBUSY; + goto out; + } +- list_del(&event->list); +- goto out; +- } +- event = NULL; +- ret = -EEXIST; ++ ret = unregister_synth_event(event); ++ if (!ret) { ++ list_del(&event->list); ++ free_synth_event(event); ++ } ++ } else ++ ret = -EEXIST; + goto out; + } else if (delete_event) { + ret = -ENOENT; +@@ -1120,29 +1109,21 @@ static int create_synth_event(int argc, + event = NULL; + goto err; + } ++ ret = register_synth_event(event); ++ if (!ret) ++ list_add(&event->list, &synth_event_list); ++ else ++ free_synth_event(event); + out: +- if (event) { +- if (delete_event) { +- ret = unregister_synth_event(event); +- add_or_delete_synth_event(event, !ret); +- } else { +- ret = register_synth_event(event); +- add_or_delete_synth_event(event, ret); +- } +- } + mutex_unlock(&synth_event_mutex); + mutex_unlock(&event_mutex); + + return ret; + err: +- mutex_unlock(&synth_event_mutex); +- mutex_unlock(&event_mutex); +- + for (i = 0; i < n_fields; i++) + free_synth_field(fields[i]); +- free_synth_event(event); + +- return ret; ++ goto out; + } + + static int release_all_synth_events(void) +@@ -1161,10 +1142,12 @@ static int release_all_synth_events(void + } + + list_for_each_entry_safe(event, e, &synth_event_list, list) { +- list_del(&event->list); +- + ret = unregister_synth_event(event); +- add_or_delete_synth_event(event, !ret); ++ if (!ret) { ++ list_del(&event->list); ++ free_synth_event(event); ++ } else ++ break; + } + mutex_unlock(&synth_event_mutex); + mutex_unlock(&event_mutex); diff --git a/queue-4.19/tracing-split-up-onmatch-action-data.patch b/queue-4.19/tracing-split-up-onmatch-action-data.patch new file mode 100644 index 00000000000..3f225bf8749 --- /dev/null +++ b/queue-4.19/tracing-split-up-onmatch-action-data.patch @@ -0,0 +1,255 @@ +From stable+bounces-43488-greg=kroah.com@vger.kernel.org Thu May 9 04:30:50 2024 +From: George Guo +Date: Thu, 9 May 2024 10:29:29 +0800 +Subject: tracing: Split up onmatch action data +To: gregkh@linuxfoundation.org, rostedt@goodmis.org, mhiramat@kernel.org, tom.zanussi@linux.intel.com +Cc: stable@vger.kernel.org, George Guo +Message-ID: <20240509022931.3513365-12-dongtai.guo@linux.dev> + +From: Tom Zanussi + +commit c3e49506a0f426a850675e39419879214060ca8b upstream. + +Currently, the onmatch action data binds the onmatch action to data +related to synthetic event generation. Since we want to allow the +onmatch handler to potentially invoke a different action, and because +we expect other handlers to generate synthetic events, we need to +separate the data related to these two functions. + +Also rename the onmatch data to something more descriptive, and create +and use common action data destroy function. + +Link: http://lkml.kernel.org/r/b9abbf9aae69fe3920cdc8ddbcaad544dd258d78.1550100284.git.tom.zanussi@linux.intel.com + +Signed-off-by: Tom Zanussi +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: George Guo +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/trace.c | 12 ++++ + kernel/trace/trace_events_hist.c | 95 +++++++++++++++++++++------------------ + 2 files changed, 63 insertions(+), 44 deletions(-) + +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -4754,6 +4754,7 @@ static const char readme_msg[] = + "\t [:size=#entries]\n" + "\t [:pause][:continue][:clear]\n" + "\t [:name=histname1]\n" ++ "\t [:.]\n" + "\t [if ]\n\n" + "\t Note, special fields can be used as well:\n" + "\t common_timestamp - to record current timestamp\n" +@@ -4799,7 +4800,16 @@ static const char readme_msg[] = + "\t The enable_hist and disable_hist triggers can be used to\n" + "\t have one event conditionally start and stop another event's\n" + "\t already-attached hist trigger. The syntax is analagous to\n" +- "\t the enable_event and disable_event triggers.\n" ++ "\t the enable_event and disable_event triggers.\n\n" ++ "\t Hist trigger handlers and actions are executed whenever a\n" ++ "\t a histogram entry is added or updated. They take the form:\n\n" ++ "\t .\n\n" ++ "\t The available handlers are:\n\n" ++ "\t onmatch(matching.event) - invoke on addition or update\n" ++ "\t onmax(var) - invoke if var exceeds current max\n\n" ++ "\t The available actions are:\n\n" ++ "\t (param list) - generate synthetic event\n" ++ "\t save(field,...) - save current event fields\n" + #endif + ; + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -379,13 +379,22 @@ struct action_data { + unsigned int n_params; + char *params[SYNTH_FIELDS_MAX]; + ++ /* ++ * When a histogram trigger is hit, the values of any ++ * references to variables, including variables being passed ++ * as parameters to synthetic events, are collected into a ++ * var_ref_vals array. This var_ref_idx is the index of the ++ * first param in the array to be passed to the synthetic ++ * event invocation. ++ */ ++ unsigned int var_ref_idx; ++ struct synth_event *synth_event; ++ + union { + struct { +- unsigned int var_ref_idx; +- char *match_event; +- char *match_event_system; +- struct synth_event *synth_event; +- } onmatch; ++ char *event; ++ char *event_system; ++ } match_data; + + struct { + char *var_str; +@@ -1080,9 +1089,9 @@ static void action_trace(struct hist_tri + struct ring_buffer_event *rbe, void *key, + struct action_data *data, u64 *var_ref_vals) + { +- struct synth_event *event = data->onmatch.synth_event; ++ struct synth_event *event = data->synth_event; + +- trace_synth(event, var_ref_vals, data->onmatch.var_ref_idx); ++ trace_synth(event, var_ref_vals, data->var_ref_idx); + } + + struct hist_var_data { +@@ -1648,8 +1657,8 @@ find_match_var(struct hist_trigger_data + struct action_data *data = hist_data->actions[i]; + + if (data->handler == HANDLER_ONMATCH) { +- char *system = data->onmatch.match_event_system; +- char *event_name = data->onmatch.match_event; ++ char *system = data->match_data.event_system; ++ char *event_name = data->match_data.event; + + file = find_var_file(tr, system, event_name, var_name); + if (!file) +@@ -3490,22 +3499,33 @@ static void onmax_save(struct hist_trigg + update_max_vars(hist_data, elt, rbe, rec); + } + +-static void onmax_destroy(struct action_data *data) ++static void action_data_destroy(struct action_data *data) + { + unsigned int i; + +- destroy_hist_field(data->onmax.max_var, 0); +- destroy_hist_field(data->onmax.var, 0); ++ lockdep_assert_held(&event_mutex); + +- kfree(data->onmax.var_str); + kfree(data->action_name); + + for (i = 0; i < data->n_params; i++) + kfree(data->params[i]); + ++ if (data->synth_event) ++ data->synth_event->ref--; ++ + kfree(data); + } + ++static void onmax_destroy(struct action_data *data) ++{ ++ destroy_hist_field(data->onmax.max_var, 0); ++ destroy_hist_field(data->onmax.var, 0); ++ ++ kfree(data->onmax.var_str); ++ ++ action_data_destroy(data); ++} ++ + static int action_create(struct hist_trigger_data *hist_data, + struct action_data *data); + +@@ -3685,21 +3705,10 @@ static struct action_data *onmax_parse(c + + static void onmatch_destroy(struct action_data *data) + { +- unsigned int i; +- +- lockdep_assert_held(&event_mutex); ++ kfree(data->match_data.event); ++ kfree(data->match_data.event_system); + +- kfree(data->onmatch.match_event); +- kfree(data->onmatch.match_event_system); +- kfree(data->action_name); +- +- for (i = 0; i < data->n_params; i++) +- kfree(data->params[i]); +- +- if (data->onmatch.synth_event) +- data->onmatch.synth_event->ref--; +- +- kfree(data); ++ action_data_destroy(data); + } + + static void destroy_field_var(struct field_var *field_var) +@@ -3760,8 +3769,8 @@ trace_action_find_var(struct hist_trigge + hist_field = find_target_event_var(hist_data, system, event, var); + if (!hist_field) { + if (!system && data->handler == HANDLER_ONMATCH) { +- system = data->onmatch.match_event_system; +- event = data->onmatch.match_event; ++ system = data->match_data.event_system; ++ event = data->match_data.event; + } + + hist_field = find_event_var(hist_data, system, event, var); +@@ -3800,8 +3809,8 @@ trace_action_create_field_var(struct his + * event. + */ + if (!system && data->handler == HANDLER_ONMATCH) { +- system = data->onmatch.match_event_system; +- event = data->onmatch.match_event; ++ system = data->match_data.event_system; ++ event = data->match_data.event; + } + + if (!event) +@@ -3913,8 +3922,8 @@ static int trace_action_create(struct hi + goto err; + } + +- data->onmatch.synth_event = event; +- data->onmatch.var_ref_idx = var_ref_idx; ++ data->synth_event = event; ++ data->var_ref_idx = var_ref_idx; + out: + return ret; + err: +@@ -4000,14 +4009,14 @@ static struct action_data *onmatch_parse + goto free; + } + +- data->onmatch.match_event = kstrdup(match_event, GFP_KERNEL); +- if (!data->onmatch.match_event) { ++ data->match_data.event = kstrdup(match_event, GFP_KERNEL); ++ if (!data->match_data.event) { + ret = -ENOMEM; + goto free; + } + +- data->onmatch.match_event_system = kstrdup(match_event_system, GFP_KERNEL); +- if (!data->onmatch.match_event_system) { ++ data->match_data.event_system = kstrdup(match_event_system, GFP_KERNEL); ++ if (!data->match_data.event_system) { + ret = -ENOMEM; + goto free; + } +@@ -4579,8 +4588,8 @@ static void print_onmatch_spec(struct se + struct hist_trigger_data *hist_data, + struct action_data *data) + { +- seq_printf(m, ":onmatch(%s.%s).", data->onmatch.match_event_system, +- data->onmatch.match_event); ++ seq_printf(m, ":onmatch(%s.%s).", data->match_data.event_system, ++ data->match_data.event); + + seq_printf(m, "%s(", data->action_name); + +@@ -4618,11 +4627,11 @@ static bool actions_match(struct hist_tr + return false; + + if (data->handler == HANDLER_ONMATCH) { +- if (strcmp(data->onmatch.match_event_system, +- data_test->onmatch.match_event_system) != 0) ++ if (strcmp(data->match_data.event_system, ++ data_test->match_data.event_system) != 0) + return false; +- if (strcmp(data->onmatch.match_event, +- data_test->onmatch.match_event) != 0) ++ if (strcmp(data->match_data.event, ++ data_test->match_data.event) != 0) + return false; + } else if (data->handler == HANDLER_ONMAX) { + if (strcmp(data->onmax.var_str, diff --git a/queue-4.19/tracing-use-dyn_event-framework-for-synthetic-events.patch b/queue-4.19/tracing-use-dyn_event-framework-for-synthetic-events.patch new file mode 100644 index 00000000000..0ca0a92abbb --- /dev/null +++ b/queue-4.19/tracing-use-dyn_event-framework-for-synthetic-events.patch @@ -0,0 +1,537 @@ +From stable+bounces-43480-greg=kroah.com@vger.kernel.org Thu May 9 04:30:20 2024 +From: George Guo +Date: Thu, 9 May 2024 10:29:21 +0800 +Subject: tracing: Use dyn_event framework for synthetic events +To: gregkh@linuxfoundation.org, rostedt@goodmis.org, mhiramat@kernel.org, tom.zanussi@linux.intel.com +Cc: stable@vger.kernel.org, George Guo +Message-ID: <20240509022931.3513365-4-dongtai.guo@linux.dev> + +From: Masami Hiramatsu + +commit 7bbab38d07f3185fddf6fce126e2239010efdfce upstream. + +Use dyn_event framework for synthetic events. This shows +synthetic events on "tracing/dynamic_events" file in addition +to tracing/synthetic_events interface. + +User can also define new events via tracing/dynamic_events +with "s:" prefix. So, the new syntax is below; + + s:[synthetic/]EVENT_NAME TYPE ARG; [TYPE ARG;]... + +To remove events via tracing/dynamic_events, you can use +"-:" prefix as same as other events. + +Link: http://lkml.kernel.org/r/154140861301.17322.15454611233735614508.stgit@devbox + +Reviewed-by: Tom Zanussi +Tested-by: Tom Zanussi +Signed-off-by: Masami Hiramatsu +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: George Guo +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/Kconfig | 1 + kernel/trace/trace.c | 8 + + kernel/trace/trace_events_hist.c | 265 ++++++++++++++++++++++++--------------- + 3 files changed, 176 insertions(+), 98 deletions(-) + +--- a/kernel/trace/Kconfig ++++ b/kernel/trace/Kconfig +@@ -633,6 +633,7 @@ config HIST_TRIGGERS + depends on ARCH_HAVE_NMI_SAFE_CMPXCHG + select TRACING_MAP + select TRACING ++ select DYNAMIC_EVENTS + default n + help + Hist triggers allow one or more arbitrary trace event fields +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -4681,6 +4681,9 @@ static const char readme_msg[] = + "\t accepts: event-definitions (one definition per line)\n" + "\t Format: p[:[/]] []\n" + "\t r[maxactive][:[/]] []\n" ++#ifdef CONFIG_HIST_TRIGGERS ++ "\t s:[synthetic/] []\n" ++#endif + "\t -:[/]\n" + #ifdef CONFIG_KPROBE_EVENTS + "\t place: [:][+]|\n" +@@ -4694,6 +4697,11 @@ static const char readme_msg[] = + "\t $stack, $stack, $retval, $comm\n" + "\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string,\n" + "\t b@/\n" ++#ifdef CONFIG_HIST_TRIGGERS ++ "\t field: ;\n" ++ "\t stype: u8/u16/u32/u64, s8/s16/s32/s64, pid_t,\n" ++ "\t [unsigned] char/int/long\n" ++#endif + #endif + " events/\t\t- Directory containing all trace event subsystems:\n" + " enable\t\t- Write 0/1 to enable/disable tracing of all events\n" +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -15,6 +15,7 @@ + + #include "tracing_map.h" + #include "trace.h" ++#include "trace_dynevent.h" + + #define SYNTH_SYSTEM "synthetic" + #define SYNTH_FIELDS_MAX 16 +@@ -291,6 +292,21 @@ struct hist_trigger_data { + unsigned int n_max_var_str; + }; + ++static int synth_event_create(int argc, const char **argv); ++static int synth_event_show(struct seq_file *m, struct dyn_event *ev); ++static int synth_event_release(struct dyn_event *ev); ++static bool synth_event_is_busy(struct dyn_event *ev); ++static bool synth_event_match(const char *system, const char *event, ++ struct dyn_event *ev); ++ ++static struct dyn_event_operations synth_event_ops = { ++ .create = synth_event_create, ++ .show = synth_event_show, ++ .is_busy = synth_event_is_busy, ++ .free = synth_event_release, ++ .match = synth_event_match, ++}; ++ + struct synth_field { + char *type; + char *name; +@@ -300,7 +316,7 @@ struct synth_field { + }; + + struct synth_event { +- struct list_head list; ++ struct dyn_event devent; + int ref; + char *name; + struct synth_field **fields; +@@ -311,6 +327,32 @@ struct synth_event { + struct tracepoint *tp; + }; + ++static bool is_synth_event(struct dyn_event *ev) ++{ ++ return ev->ops == &synth_event_ops; ++} ++ ++static struct synth_event *to_synth_event(struct dyn_event *ev) ++{ ++ return container_of(ev, struct synth_event, devent); ++} ++ ++static bool synth_event_is_busy(struct dyn_event *ev) ++{ ++ struct synth_event *event = to_synth_event(ev); ++ ++ return event->ref != 0; ++} ++ ++static bool synth_event_match(const char *system, const char *event, ++ struct dyn_event *ev) ++{ ++ struct synth_event *sev = to_synth_event(ev); ++ ++ return strcmp(sev->name, event) == 0 && ++ (!system || strcmp(system, SYNTH_SYSTEM) == 0); ++} ++ + struct action_data; + + typedef void (*action_fn_t) (struct hist_trigger_data *hist_data, +@@ -401,7 +443,6 @@ static bool have_hist_err(void) + return false; + } + +-static LIST_HEAD(synth_event_list); + static DEFINE_MUTEX(synth_event_mutex); + + struct synth_trace_event { +@@ -758,14 +799,12 @@ static void free_synth_field(struct synt + kfree(field); + } + +-static struct synth_field *parse_synth_field(int argc, char **argv, ++static struct synth_field *parse_synth_field(int argc, const char **argv, + int *consumed) + { + struct synth_field *field; +- const char *prefix = NULL; +- char *field_type = argv[0], *field_name; ++ const char *prefix = NULL, *field_type = argv[0], *field_name, *array; + int len, ret = 0; +- char *array; + + if (field_type[0] == ';') + field_type++; +@@ -782,20 +821,31 @@ static struct synth_field *parse_synth_f + *consumed = 2; + } + +- 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; ++ len = strlen(field_name); + array = strchr(field_name, '['); + if (array) ++ len -= strlen(array); ++ else if (field_name[len - 1] == ';') ++ len--; ++ ++ field->name = kmemdup_nul(field_name, len, GFP_KERNEL); ++ if (!field->name) { ++ ret = -ENOMEM; ++ goto free; ++ } ++ ++ if (field_type[0] == ';') ++ field_type++; ++ len = strlen(field_type) + 1; ++ if (array) + len += strlen(array); + if (prefix) + len += strlen(prefix); ++ + field->type = kzalloc(len, GFP_KERNEL); + if (!field->type) { + ret = -ENOMEM; +@@ -806,7 +856,8 @@ static struct synth_field *parse_synth_f + strcat(field->type, field_type); + if (array) { + strcat(field->type, array); +- *array = '\0'; ++ if (field->type[len - 1] == ';') ++ field->type[len - 1] = '\0'; + } + + field->size = synth_field_size(field->type); +@@ -820,11 +871,6 @@ static struct synth_field *parse_synth_f + + 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: +@@ -888,9 +934,13 @@ static inline void trace_synth(struct sy + + static struct synth_event *find_synth_event(const char *name) + { ++ struct dyn_event *pos; + struct synth_event *event; + +- list_for_each_entry(event, &synth_event_list, list) { ++ for_each_dyn_event(pos) { ++ if (!is_synth_event(pos)) ++ continue; ++ event = to_synth_event(pos); + if (strcmp(event->name, name) == 0) + return event; + } +@@ -941,7 +991,7 @@ static int register_synth_event(struct s + + ret = set_synth_event_print_fmt(call); + if (ret < 0) { +- trace_remove_event_call(call); ++ trace_remove_event_call_nolock(call); + goto err; + } + out: +@@ -979,7 +1029,7 @@ static void free_synth_event(struct synt + kfree(event); + } + +-static struct synth_event *alloc_synth_event(char *event_name, int n_fields, ++static struct synth_event *alloc_synth_event(const char *name, int n_fields, + struct synth_field **fields) + { + struct synth_event *event; +@@ -991,7 +1041,7 @@ static struct synth_event *alloc_synth_e + goto out; + } + +- event->name = kstrdup(event_name, GFP_KERNEL); ++ event->name = kstrdup(name, GFP_KERNEL); + if (!event->name) { + kfree(event); + event = ERR_PTR(-ENOMEM); +@@ -1005,6 +1055,8 @@ static struct synth_event *alloc_synth_e + goto out; + } + ++ dyn_event_init(&event->devent, &synth_event_ops); ++ + for (i = 0; i < n_fields; i++) + event->fields[i] = fields[i]; + +@@ -1028,16 +1080,11 @@ struct hist_var_data { + struct hist_trigger_data *hist_data; + }; + +-static int create_synth_event(int argc, char **argv) ++static int __create_synth_event(int argc, const char *name, const char **argv) + { + struct synth_field *field, *fields[SYNTH_FIELDS_MAX]; + struct synth_event *event = NULL; +- bool delete_event = false; + int i, consumed = 0, n_fields = 0, ret = 0; +- char *name; +- +- mutex_lock(&event_mutex); +- mutex_lock(&synth_event_mutex); + + /* + * Argument syntax: +@@ -1045,43 +1092,20 @@ static int create_synth_event(int argc, + * - Remove synthetic event: ! field[;field] ... + * where 'field' = type field_name + */ +- if (argc < 1) { +- ret = -EINVAL; +- goto out; +- } + +- name = argv[0]; +- if (name[0] == '!') { +- delete_event = true; +- name++; +- } ++ if (name[0] == '\0' || argc < 1) ++ return -EINVAL; ++ ++ mutex_lock(&event_mutex); ++ mutex_lock(&synth_event_mutex); + + event = find_synth_event(name); + if (event) { +- if (delete_event) { +- if (event->ref) { +- ret = -EBUSY; +- goto out; +- } +- ret = unregister_synth_event(event); +- if (!ret) { +- list_del(&event->list); +- free_synth_event(event); +- } +- } else +- ret = -EEXIST; +- goto out; +- } else if (delete_event) { +- ret = -ENOENT; ++ ret = -EEXIST; + goto out; + } + +- if (argc < 2) { +- ret = -EINVAL; +- goto out; +- } +- +- for (i = 1; i < argc - 1; i++) { ++ for (i = 0; i < argc - 1; i++) { + if (strcmp(argv[i], ";") == 0) + continue; + if (n_fields == SYNTH_FIELDS_MAX) { +@@ -1111,7 +1135,7 @@ static int create_synth_event(int argc, + } + ret = register_synth_event(event); + if (!ret) +- list_add(&event->list, &synth_event_list); ++ dyn_event_add(&event->devent); + else + free_synth_event(event); + out: +@@ -1126,57 +1150,77 @@ static int create_synth_event(int argc, + goto out; + } + +-static int release_all_synth_events(void) ++static int create_or_delete_synth_event(int argc, char **argv) + { +- struct synth_event *event, *e; +- int ret = 0; +- +- mutex_lock(&event_mutex); +- mutex_lock(&synth_event_mutex); +- +- list_for_each_entry(event, &synth_event_list, list) { +- if (event->ref) { +- mutex_unlock(&synth_event_mutex); +- return -EBUSY; +- } +- } ++ const char *name = argv[0]; ++ struct synth_event *event = NULL; ++ int ret; + +- list_for_each_entry_safe(event, e, &synth_event_list, list) { +- ret = unregister_synth_event(event); +- if (!ret) { +- list_del(&event->list); +- free_synth_event(event); ++ /* trace_run_command() ensures argc != 0 */ ++ if (name[0] == '!') { ++ mutex_lock(&event_mutex); ++ mutex_lock(&synth_event_mutex); ++ event = find_synth_event(name + 1); ++ if (event) { ++ if (event->ref) ++ ret = -EBUSY; ++ else { ++ ret = unregister_synth_event(event); ++ if (!ret) { ++ dyn_event_remove(&event->devent); ++ free_synth_event(event); ++ } ++ } + } else +- break; ++ ret = -ENOENT; ++ mutex_unlock(&synth_event_mutex); ++ mutex_unlock(&event_mutex); ++ return ret; + } +- mutex_unlock(&synth_event_mutex); +- mutex_unlock(&event_mutex); + +- return ret; ++ ret = __create_synth_event(argc - 1, name, (const char **)argv + 1); ++ return ret == -ECANCELED ? -EINVAL : ret; + } + +- +-static void *synth_events_seq_start(struct seq_file *m, loff_t *pos) ++static int synth_event_create(int argc, const char **argv) + { +- mutex_lock(&synth_event_mutex); ++ const char *name = argv[0]; ++ int len; + +- return seq_list_start(&synth_event_list, *pos); ++ if (name[0] != 's' || name[1] != ':') ++ return -ECANCELED; ++ name += 2; ++ ++ /* This interface accepts group name prefix */ ++ if (strchr(name, '/')) { ++ len = sizeof(SYNTH_SYSTEM "/") - 1; ++ if (strncmp(name, SYNTH_SYSTEM "/", len)) ++ return -EINVAL; ++ name += len; ++ } ++ return __create_synth_event(argc - 1, name, argv + 1); + } + +-static void *synth_events_seq_next(struct seq_file *m, void *v, loff_t *pos) ++static int synth_event_release(struct dyn_event *ev) + { +- return seq_list_next(v, &synth_event_list, pos); +-} ++ struct synth_event *event = to_synth_event(ev); ++ int ret; + +-static void synth_events_seq_stop(struct seq_file *m, void *v) +-{ +- mutex_unlock(&synth_event_mutex); ++ if (event->ref) ++ return -EBUSY; ++ ++ ret = unregister_synth_event(event); ++ if (ret) ++ return ret; ++ ++ dyn_event_remove(ev); ++ free_synth_event(event); ++ return 0; + } + +-static int synth_events_seq_show(struct seq_file *m, void *v) ++static int __synth_event_show(struct seq_file *m, struct synth_event *event) + { + struct synth_field *field; +- struct synth_event *event = v; + unsigned int i; + + seq_printf(m, "%s\t", event->name); +@@ -1194,11 +1238,30 @@ static int synth_events_seq_show(struct + return 0; + } + ++static int synth_event_show(struct seq_file *m, struct dyn_event *ev) ++{ ++ struct synth_event *event = to_synth_event(ev); ++ ++ seq_printf(m, "s:%s/", event->class.system); ++ ++ return __synth_event_show(m, event); ++} ++ ++static int synth_events_seq_show(struct seq_file *m, void *v) ++{ ++ struct dyn_event *ev = v; ++ ++ if (!is_synth_event(ev)) ++ return 0; ++ ++ return __synth_event_show(m, to_synth_event(ev)); ++} ++ + 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 ++ .start = dyn_event_seq_start, ++ .next = dyn_event_seq_next, ++ .stop = dyn_event_seq_stop, ++ .show = synth_events_seq_show, + }; + + static int synth_events_open(struct inode *inode, struct file *file) +@@ -1206,7 +1269,7 @@ static int synth_events_open(struct inod + int ret; + + if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { +- ret = release_all_synth_events(); ++ ret = dyn_events_release_all(&synth_event_ops); + if (ret < 0) + return ret; + } +@@ -1219,7 +1282,7 @@ static ssize_t synth_events_write(struct + size_t count, loff_t *ppos) + { + return trace_parse_run_command(file, buffer, count, ppos, +- create_synth_event); ++ create_or_delete_synth_event); + } + + static const struct file_operations synth_events_fops = { +@@ -5913,6 +5976,12 @@ static __init int trace_events_hist_init + struct dentry *d_tracer; + int err = 0; + ++ err = dyn_event_register(&synth_event_ops); ++ if (err) { ++ pr_warn("Could not register synth_event_ops\n"); ++ return err; ++ } ++ + d_tracer = tracing_init_dentry(); + if (IS_ERR(d_tracer)) { + err = PTR_ERR(d_tracer); diff --git a/queue-4.19/tracing-use-str_has_prefix-helper-for-histogram-code.patch b/queue-4.19/tracing-use-str_has_prefix-helper-for-histogram-code.patch new file mode 100644 index 00000000000..399dfc47334 --- /dev/null +++ b/queue-4.19/tracing-use-str_has_prefix-helper-for-histogram-code.patch @@ -0,0 +1,125 @@ +From stable+bounces-43484-greg=kroah.com@vger.kernel.org Thu May 9 04:30:32 2024 +From: George Guo +Date: Thu, 9 May 2024 10:29:25 +0800 +Subject: tracing: Use str_has_prefix() helper for histogram code +To: gregkh@linuxfoundation.org, rostedt@goodmis.org, mhiramat@kernel.org, tom.zanussi@linux.intel.com +Cc: stable@vger.kernel.org, Namhyung Kim , George Guo +Message-ID: <20240509022931.3513365-8-dongtai.guo@linux.dev> + +From: "Steven Rostedt (VMware)" + +commit 754481e6954cbef53f8bc4412ad48dde611e21d3 upstream. + +The tracing histogram code contains a lot of instances of the construct: + + strncmp(str, "const", sizeof("const") - 1) + +This can be prone to bugs due to typos or bad cut and paste. Use the +str_has_prefix() helper macro instead that removes the need for having two +copies of the constant string. + +Cc: Tom Zanussi +Acked-by: Namhyung Kim +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: George Guo +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/trace_events_hist.c | 32 ++++++++++++++++---------------- + 1 file changed, 16 insertions(+), 16 deletions(-) + +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -1878,8 +1878,8 @@ static int parse_action(char *str, struc + if (attrs->n_actions >= HIST_ACTIONS_MAX) + return ret; + +- if ((strncmp(str, "onmatch(", strlen("onmatch(")) == 0) || +- (strncmp(str, "onmax(", strlen("onmax(")) == 0)) { ++ if ((str_has_prefix(str, "onmatch(")) || ++ (str_has_prefix(str, "onmax("))) { + attrs->action_str[attrs->n_actions] = kstrdup(str, GFP_KERNEL); + if (!attrs->action_str[attrs->n_actions]) { + ret = -ENOMEM; +@@ -1896,34 +1896,34 @@ static int parse_assignment(char *str, s + { + int ret = 0; + +- if ((strncmp(str, "key=", strlen("key=")) == 0) || +- (strncmp(str, "keys=", strlen("keys=")) == 0)) { ++ if ((str_has_prefix(str, "key=")) || ++ (str_has_prefix(str, "keys="))) { + attrs->keys_str = kstrdup(str, GFP_KERNEL); + if (!attrs->keys_str) { + ret = -ENOMEM; + goto out; + } +- } else if ((strncmp(str, "val=", strlen("val=")) == 0) || +- (strncmp(str, "vals=", strlen("vals=")) == 0) || +- (strncmp(str, "values=", strlen("values=")) == 0)) { ++ } else if ((str_has_prefix(str, "val=")) || ++ (str_has_prefix(str, "vals=")) || ++ (str_has_prefix(str, "values="))) { + attrs->vals_str = kstrdup(str, GFP_KERNEL); + if (!attrs->vals_str) { + ret = -ENOMEM; + goto out; + } +- } else if (strncmp(str, "sort=", strlen("sort=")) == 0) { ++ } else if (str_has_prefix(str, "sort=")) { + attrs->sort_key_str = kstrdup(str, GFP_KERNEL); + if (!attrs->sort_key_str) { + ret = -ENOMEM; + goto out; + } +- } else if (strncmp(str, "name=", strlen("name=")) == 0) { ++ } else if (str_has_prefix(str, "name=")) { + attrs->name = kstrdup(str, GFP_KERNEL); + if (!attrs->name) { + ret = -ENOMEM; + goto out; + } +- } else if (strncmp(str, "clock=", strlen("clock=")) == 0) { ++ } else if (str_has_prefix(str, "clock=")) { + strsep(&str, "="); + if (!str) { + ret = -EINVAL; +@@ -1936,7 +1936,7 @@ static int parse_assignment(char *str, s + ret = -ENOMEM; + goto out; + } +- } else if (strncmp(str, "size=", strlen("size=")) == 0) { ++ } else if (str_has_prefix(str, "size=")) { + int map_bits = parse_map_size(str); + + if (map_bits < 0) { +@@ -3623,7 +3623,7 @@ static struct action_data *onmax_parse(c + if (!onmax_fn_name || !str) + goto free; + +- if (strncmp(onmax_fn_name, "save", strlen("save")) == 0) { ++ if (str_has_prefix(onmax_fn_name, "save")) { + char *params = strsep(&str, ")"); + + if (!params) { +@@ -4414,8 +4414,8 @@ static int parse_actions(struct hist_tri + 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("); ++ if (str_has_prefix(str, "onmatch(")) { ++ char *action_str = str + sizeof("onmatch(") - 1; + + data = onmatch_parse(tr, action_str); + if (IS_ERR(data)) { +@@ -4423,8 +4423,8 @@ static int parse_actions(struct hist_tri + break; + } + data->fn = action_trace; +- } else if (strncmp(str, "onmax(", strlen("onmax(")) == 0) { +- char *action_str = str + strlen("onmax("); ++ } else if (str_has_prefix(str, "onmax(")) { ++ char *action_str = str + sizeof("onmax(") - 1; + + data = onmax_parse(action_str); + if (IS_ERR(data)) { diff --git a/queue-4.19/tracing-use-str_has_prefix-instead-of-using-fixed-sizes.patch b/queue-4.19/tracing-use-str_has_prefix-instead-of-using-fixed-sizes.patch new file mode 100644 index 00000000000..9a978e95f7c --- /dev/null +++ b/queue-4.19/tracing-use-str_has_prefix-instead-of-using-fixed-sizes.patch @@ -0,0 +1,83 @@ +From stable+bounces-43485-greg=kroah.com@vger.kernel.org Thu May 9 04:30:44 2024 +From: George Guo +Date: Thu, 9 May 2024 10:29:26 +0800 +Subject: tracing: Use str_has_prefix() instead of using fixed sizes +To: gregkh@linuxfoundation.org, rostedt@goodmis.org, mhiramat@kernel.org, tom.zanussi@linux.intel.com +Cc: stable@vger.kernel.org, Namhyung Kim , George Guo +Message-ID: <20240509022931.3513365-9-dongtai.guo@linux.dev> + +From: "Steven Rostedt (VMware)" + +commit b6b2735514bcd70ad1556a33892a636b20ece671 upstream. + +There are several instances of strncmp(str, "const", 123), where 123 is the +strlen of the const string to check if "const" is the prefix of str. But +this can be error prone. Use str_has_prefix() instead. + +Acked-by: Namhyung Kim +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: George Guo +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/trace.c | 2 +- + kernel/trace/trace_events.c | 2 +- + kernel/trace/trace_events_hist.c | 2 +- + kernel/trace/trace_probe.c | 2 +- + kernel/trace/trace_stack.c | 2 +- + 5 files changed, 5 insertions(+), 5 deletions(-) + +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -4470,7 +4470,7 @@ static int trace_set_options(struct trac + + cmp = strstrip(option); + +- if (strncmp(cmp, "no", 2) == 0) { ++ if (str_has_prefix(cmp, "no")) { + neg = 1; + cmp += 2; + } +--- a/kernel/trace/trace_events.c ++++ b/kernel/trace/trace_events.c +@@ -1249,7 +1249,7 @@ static int f_show(struct seq_file *m, vo + */ + array_descriptor = strchr(field->type, '['); + +- if (!strncmp(field->type, "__data_loc", 10)) ++ if (str_has_prefix(field->type, "__data_loc")) + array_descriptor = NULL; + + if (!array_descriptor) +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -484,7 +484,7 @@ static int synth_event_define_fields(str + + static bool synth_field_signed(char *type) + { +- if (strncmp(type, "u", 1) == 0) ++ if (str_has_prefix(type, "u")) + return false; + if (strcmp(type, "gfp_t") == 0) + return false; +--- a/kernel/trace/trace_probe.c ++++ b/kernel/trace/trace_probe.c +@@ -342,7 +342,7 @@ static int parse_probe_vars(char *arg, c + f->fn = t->fetch[FETCH_MTD_retval]; + else + ret = -EINVAL; +- } else if (strncmp(arg, "stack", 5) == 0) { ++ } else if (str_has_prefix(arg, "stack")) { + if (arg[5] == '\0') { + if (strcmp(t->name, DEFAULT_FETCH_TYPE_STR)) + return -EINVAL; +--- a/kernel/trace/trace_stack.c ++++ b/kernel/trace/trace_stack.c +@@ -453,7 +453,7 @@ static char stack_trace_filter_buf[COMMA + + static __init int enable_stacktrace(char *str) + { +- if (strncmp(str, "_filter=", 8) == 0) ++ if (str_has_prefix(str, "_filter=")) + strncpy(stack_trace_filter_buf, str+8, COMMAND_LINE_SIZE); + + stack_tracer_enabled = 1; -- 2.47.3