--- /dev/null
+From 9af503d91298c3f2945e73703f0e00995be08c30 Mon Sep 17 00:00:00 2001
+From: Dominique Martinet <dominique.martinet@atmark-techno.com>
+Date: Fri, 19 Apr 2024 11:22:48 +0900
+Subject: btrfs: add missing mutex_unlock in btrfs_relocate_sys_chunks()
+
+From: Dominique Martinet <dominique.martinet@atmark-techno.com>
+
+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 <pavel@denx.de>
+Fixes: 7411055db5ce ("btrfs: handle chunk tree lookup error in btrfs_relocate_sys_chunks()")
+CC: stable@vger.kernel.org
+Reviewed-by: Pavel Machek <pavel@denx.de>
+Signed-off-by: Dominique Martinet <dominique.martinet@atmark-techno.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Dominique Martinet <dominique.martinet@atmark-techno.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
+ }
+
--- /dev/null
+From d43ddd5c91802a46354fa4c4381416ef760676e2 Mon Sep 17 00:00:00 2001
+From: Akira Yokosawa <akiyks@gmail.com>
+Date: Wed, 1 May 2024 12:16:11 +0900
+Subject: docs: kernel_include.py: Cope with docutils 0.21
+
+From: Akira Yokosawa <akiyks@gmail.com>
+
+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 <akiyks@gmail.com>
+Cc: stable@vger.kernel.org
+Signed-off-by: Jonathan Corbet <corbet@lwn.net>
+Link: https://lore.kernel.org/r/faf5fa45-2a9d-4573-9d2e-3930bdc1ed65@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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
--- /dev/null
+From b2aba15ad6f908d1a620fd97f6af5620c3639742 Mon Sep 17 00:00:00 2001
+From: Daniel Thompson <daniel.thompson@linaro.org>
+Date: Wed, 24 Apr 2024 15:21:41 +0100
+Subject: serial: kgdboc: Fix NMI-safety problems from keyboard reset code
+
+From: Daniel Thompson <daniel.thompson@linaro.org>
+
+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 <liu.yeC@h3c.com>
+Closes: https://lore.kernel.org/all/20240228025602.3087748-1-liu.yeC@h3c.com/
+Cc: stable@vger.kernel.org
+Reviewed-by: Douglas Anderson <dianders@chromium.org>
+Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Link: https://lore.kernel.org/r/20240424-kgdboc_fix_schedule_work-v2-1-50f5a490aec5@linaro.org
+Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 <linux/console.h>
+ #include <linux/vt_kern.h>
+ #include <linux/input.h>
++#include <linux/irq_work.h>
+ #include <linux/module.h>
+
+ #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 */
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
--- /dev/null
+From stable+bounces-43483-greg=kroah.com@vger.kernel.org Thu May 9 04:30:28 2024
+From: George Guo <dongtai.guo@linux.dev>
+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 <zanussi@kernel.org>, Namhyung Kim <namhyung@kernel.org>, Linus Torvalds <torvalds@linux-foundation.org>, Joe Perches <joe@perches.com>, Andreas Schwab <schwab@linux-m68k.org>, George Guo <guodongtai@kylinos.cn>
+Message-ID: <20240509022931.3513365-7-dongtai.guo@linux.dev>
+
+From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
+
+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 <zanussi@kernel.org>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Acked-by: Namhyung Kim <namhyung@kernel.org>
+Suggestions-by: Linus Torvalds <torvalds@linux-foundation.org>
+Suggestions-by: Joe Perches <joe@perches.com>
+Suggestions-by: Andreas Schwab <schwab@linux-m68k.org>
+Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
+Signed-off-by: George Guo <guodongtai@kylinos.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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_ */
--- /dev/null
+From stable+bounces-43479-greg=kroah.com@vger.kernel.org Thu May 9 04:30:16 2024
+From: George Guo <dongtai.guo@linux.dev>
+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 <guodongtai@kylinos.cn>
+Message-ID: <20240509022931.3513365-3-dongtai.guo@linux.dev>
+
+From: Masami Hiramatsu <mhiramat@kernel.org>
+
+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 <tom.zanussi@linux.intel.com>
+Tested-by: Tom Zanussi <tom.zanussi@linux.intel.com>
+Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
+Signed-off-by: George Guo <guodongtai@kylinos.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 <mhiramat@kernel.org>
++ */
++
++#include <linux/debugfs.h>
++#include <linux/kernel.h>
++#include <linux/list.h>
++#include <linux/mm.h>
++#include <linux/mutex.h>
++#include <linux/tracefs.h>
++
++#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 <linux/kernel.h>
++#include <linux/list.h>
++#include <linux/mutex.h>
++#include <linux/seq_file.h>
++
++#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
--- /dev/null
+From stable+bounces-43482-greg=kroah.com@vger.kernel.org Thu May 9 04:30:26 2024
+From: George Guo <dongtai.guo@linux.dev>
+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 <guodongtai@kylinos.cn>
+Message-ID: <20240509022931.3513365-6-dongtai.guo@linux.dev>
+
+From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
+
+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 <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
+Signed-off-by: George Guo <guodongtai@kylinos.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
+ }
--- /dev/null
+From stable+bounces-43489-greg=kroah.com@vger.kernel.org Thu May 9 04:30:50 2024
+From: George Guo <dongtai.guo@linux.dev>
+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 <guodongtai@kylinos.cn>
+Message-ID: <20240509022931.3513365-13-dongtai.guo@linux.dev>
+
+From: Tom Zanussi <tom.zanussi@linux.intel.com>
+
+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 <tom.zanussi@linux.intel.com>
+Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
+Signed-off-by: George Guo <guodongtai@kylinos.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);
+ }
+ }
+
--- /dev/null
+From stable+bounces-43486-greg=kroah.com@vger.kernel.org Thu May 9 04:30:45 2024
+From: George Guo <dongtai.guo@linux.dev>
+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 <zanussi@kernel.org>, Namhyung Kim <namhyung@kernel.org>, George Guo <guodongtai@kylinos.cn>
+Message-ID: <20240509022931.3513365-10-dongtai.guo@linux.dev>
+
+From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
+
+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 <zanussi@kernel.org>
+Acked-by: Namhyung Kim <namhyung@kernel.org>
+Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
+Signed-off-by: George Guo <guodongtai@kylinos.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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)) {
--- /dev/null
+From stable+bounces-43487-greg=kroah.com@vger.kernel.org Thu May 9 04:30:46 2024
+From: George Guo <dongtai.guo@linux.dev>
+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 <guodongtai@kylinos.cn>
+Message-ID: <20240509022931.3513365-11-dongtai.guo@linux.dev>
+
+From: Tom Zanussi <tom.zanussi@linux.intel.com>
+
+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 <tom.zanussi@linux.intel.com>
+Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
+Signed-off-by: George Guo <guodongtai@kylinos.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
+
--- /dev/null
+From stable+bounces-43490-greg=kroah.com@vger.kernel.org Thu May 9 04:30:50 2024
+From: George Guo <dongtai.guo@linux.dev>
+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 <guodongtai@kylinos.cn>
+Message-ID: <20240509022931.3513365-14-dongtai.guo@linux.dev>
+
+From: Tom Zanussi <tom.zanussi@linux.intel.com>
+
+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) <rostedt@goodmis.org>
+Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
+Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
+Signed-off-by: George Guo <guodongtai@kylinos.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);
+
--- /dev/null
+From stable+bounces-43481-greg=kroah.com@vger.kernel.org Thu May 9 04:30:22 2024
+From: George Guo <dongtai.guo@linux.dev>
+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 <guodongtai@kylinos.cn>
+Message-ID: <20240509022931.3513365-5-dongtai.guo@linux.dev>
+
+From: Masami Hiramatsu <mhiramat@kernel.org>
+
+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 <tom.zanussi@linux.intel.com>
+Tested-by: Tom Zanussi <tom.zanussi@linux.intel.com>
+Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
+Signed-off-by: George Guo <guodongtai@kylinos.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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:
--- /dev/null
+From stable+bounces-43478-greg=kroah.com@vger.kernel.org Thu May 9 04:30:13 2024
+From: George Guo <dongtai.guo@linux.dev>
+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 <guodongtai@kylinos.cn>
+Message-ID: <20240509022931.3513365-2-dongtai.guo@linux.dev>
+
+From: Masami Hiramatsu <mhiramat@kernel.org>
+
+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 <tom.zanussi@linux.intel.com>
+Tested-by: Tom Zanussi <tom.zanussi@linux.intel.com>
+Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
+Signed-off-by: George Guo <guodongtai@kylinos.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);
--- /dev/null
+From stable+bounces-43488-greg=kroah.com@vger.kernel.org Thu May 9 04:30:50 2024
+From: George Guo <dongtai.guo@linux.dev>
+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 <guodongtai@kylinos.cn>
+Message-ID: <20240509022931.3513365-12-dongtai.guo@linux.dev>
+
+From: Tom Zanussi <tom.zanussi@linux.intel.com>
+
+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 <tom.zanussi@linux.intel.com>
+Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
+Signed-off-by: George Guo <guodongtai@kylinos.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 [:<handler>.<action>]\n"
+ "\t [if <filter>]\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 <handler>.<action>\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 <synthetic_event>(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,
--- /dev/null
+From stable+bounces-43480-greg=kroah.com@vger.kernel.org Thu May 9 04:30:20 2024
+From: George Guo <dongtai.guo@linux.dev>
+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 <guodongtai@kylinos.cn>
+Message-ID: <20240509022931.3513365-4-dongtai.guo@linux.dev>
+
+From: Masami Hiramatsu <mhiramat@kernel.org>
+
+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 <tom.zanussi@linux.intel.com>
+Tested-by: Tom Zanussi <tom.zanussi@linux.intel.com>
+Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
+Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
+Signed-off-by: George Guo <guodongtai@kylinos.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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[:[<group>/]<event>] <place> [<args>]\n"
+ "\t r[maxactive][:[<group>/]<event>] <place> [<args>]\n"
++#ifdef CONFIG_HIST_TRIGGERS
++ "\t s:[synthetic/]<event> <field> [<field>]\n"
++#endif
+ "\t -:[<group>/]<event>\n"
+ #ifdef CONFIG_KPROBE_EVENTS
+ "\t place: [<module>:]<symbol>[+<offset>]|<memaddr>\n"
+@@ -4694,6 +4697,11 @@ static const char readme_msg[] =
+ "\t $stack<index>, $stack, $retval, $comm\n"
+ "\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string,\n"
+ "\t b<bit-width>@<bit-offset>/<container-size>\n"
++#ifdef CONFIG_HIST_TRIGGERS
++ "\t field: <stype> <name>;\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: !<event_name> 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);
--- /dev/null
+From stable+bounces-43484-greg=kroah.com@vger.kernel.org Thu May 9 04:30:32 2024
+From: George Guo <dongtai.guo@linux.dev>
+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 <namhyung@kernel.org>, George Guo <guodongtai@kylinos.cn>
+Message-ID: <20240509022931.3513365-8-dongtai.guo@linux.dev>
+
+From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
+
+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 <tom.zanussi@linux.intel.com>
+Acked-by: Namhyung Kim <namhyung@kernel.org>
+Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
+Signed-off-by: George Guo <guodongtai@kylinos.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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)) {
--- /dev/null
+From stable+bounces-43485-greg=kroah.com@vger.kernel.org Thu May 9 04:30:44 2024
+From: George Guo <dongtai.guo@linux.dev>
+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 <namhyung@kernel.org>, George Guo <guodongtai@kylinos.cn>
+Message-ID: <20240509022931.3513365-9-dongtai.guo@linux.dev>
+
+From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
+
+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 <namhyung@kernel.org>
+Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
+Signed-off-by: George Guo <guodongtai@kylinos.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;