]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees
authorSasha Levin <sashal@kernel.org>
Sun, 24 May 2026 15:21:55 +0000 (11:21 -0400)
committerSasha Levin <sashal@kernel.org>
Sun, 24 May 2026 15:21:55 +0000 (11:21 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-5.10/revert-x86-vdso-fix-output-operand-size-of-rdpid.patch [new file with mode: 0644]
queue-5.10/series
queue-5.15/revert-x86-vdso-fix-output-operand-size-of-rdpid.patch [new file with mode: 0644]
queue-5.15/series
queue-6.12/revert-perf-cgroup-update-metric-leader-in-evlist__e.patch [new file with mode: 0644]
queue-6.12/revert-perf-python-add-parse_events-function.patch [new file with mode: 0644]
queue-6.12/revert-perf-tool_pmu-factor-tool-events-into-their-o.patch [new file with mode: 0644]
queue-6.12/revert-perf-tool_pmu-fix-aggregation-on-duration_tim.patch [new file with mode: 0644]
queue-6.12/series
queue-6.18/fuse-fix-uninit-value-in-fuse_dentry_revalidate.patch [new file with mode: 0644]
queue-6.18/series

diff --git a/queue-5.10/revert-x86-vdso-fix-output-operand-size-of-rdpid.patch b/queue-5.10/revert-x86-vdso-fix-output-operand-size-of-rdpid.patch
new file mode 100644 (file)
index 0000000..b5cef3c
--- /dev/null
@@ -0,0 +1,42 @@
+From 32600af84e48563ff5139c5c3d6b594369d3c5c8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 24 May 2026 10:02:23 -0400
+Subject: Revert "x86/vdso: Fix output operand size of RDPID"
+
+This reverts commit f097ba74116fce394160c919bb2039b60fc64159.
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/include/asm/segment.h | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/arch/x86/include/asm/segment.h b/arch/x86/include/asm/segment.h
+index 8686f5cfbc6b7..72044026eb3c2 100644
+--- a/arch/x86/include/asm/segment.h
++++ b/arch/x86/include/asm/segment.h
+@@ -242,7 +242,7 @@ static inline unsigned long vdso_encode_cpunode(int cpu, unsigned long node)
+ static inline void vdso_read_cpunode(unsigned *cpu, unsigned *node)
+ {
+-      unsigned long p;
++      unsigned int p;
+       /*
+        * Load CPU and node number from the GDT.  LSL is faster than RDTSCP
+@@ -252,10 +252,10 @@ static inline void vdso_read_cpunode(unsigned *cpu, unsigned *node)
+        *
+        * If RDPID is available, use it.
+        */
+-      alternative_io ("lsl %[seg],%k[p]",
+-                      "rdpid %[p]",
++      alternative_io ("lsl %[seg],%[p]",
++                      ".byte 0xf3,0x0f,0xc7,0xf8", /* RDPID %eax/rax */
+                       X86_FEATURE_RDPID,
+-                      [p] "=r" (p), [seg] "r" (__CPUNODE_SEG));
++                      [p] "=a" (p), [seg] "r" (__CPUNODE_SEG));
+       if (cpu)
+               *cpu = (p & VDSO_CPUNODE_MASK);
+-- 
+2.53.0
+
index 18d017436bae5b2db89f6a5cae8964bb10ff743a..07a3e8f73890e3383447d1850a58922cfbcb92d7 100644 (file)
@@ -527,3 +527,4 @@ io-wq-check-that-the-predecessor-is-hashed-in-io_wq_remove_pending.patch
 net-rds-reset-op_nents-when-zerocopy-page-pin-fails.patch
 s390-debug-reject-zero-length-input-before-trimming-.patch
 selftests-lib.mk-also-install-config-and-settings.patch
+revert-x86-vdso-fix-output-operand-size-of-rdpid.patch
diff --git a/queue-5.15/revert-x86-vdso-fix-output-operand-size-of-rdpid.patch b/queue-5.15/revert-x86-vdso-fix-output-operand-size-of-rdpid.patch
new file mode 100644 (file)
index 0000000..cf8dff1
--- /dev/null
@@ -0,0 +1,42 @@
+From 3693047bc8cebd45a594b6bf345affc275538e9a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 24 May 2026 10:02:01 -0400
+Subject: Revert "x86/vdso: Fix output operand size of RDPID"
+
+This reverts commit 64f14b1ab6f39a704b62bf9b3fa28803cf2b3ebe.
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/include/asm/segment.h | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/arch/x86/include/asm/segment.h b/arch/x86/include/asm/segment.h
+index 879be4ffa06c7..8dd8e8ec9fa55 100644
+--- a/arch/x86/include/asm/segment.h
++++ b/arch/x86/include/asm/segment.h
+@@ -242,7 +242,7 @@ static inline unsigned long vdso_encode_cpunode(int cpu, unsigned long node)
+ static inline void vdso_read_cpunode(unsigned *cpu, unsigned *node)
+ {
+-      unsigned long p;
++      unsigned int p;
+       /*
+        * Load CPU and node number from the GDT.  LSL is faster than RDTSCP
+@@ -252,10 +252,10 @@ static inline void vdso_read_cpunode(unsigned *cpu, unsigned *node)
+        *
+        * If RDPID is available, use it.
+        */
+-      alternative_io ("lsl %[seg],%k[p]",
+-                      "rdpid %[p]",
++      alternative_io ("lsl %[seg],%[p]",
++                      ".byte 0xf3,0x0f,0xc7,0xf8", /* RDPID %eax/rax */
+                       X86_FEATURE_RDPID,
+-                      [p] "=r" (p), [seg] "r" (__CPUNODE_SEG));
++                      [p] "=a" (p), [seg] "r" (__CPUNODE_SEG));
+       if (cpu)
+               *cpu = (p & VDSO_CPUNODE_MASK);
+-- 
+2.53.0
+
index fe706386e2f0d48fcc147f7cc989ed6f5f44f9a6..4dc640591534bb3616efd65b40eaa841afdf2670 100644 (file)
@@ -674,3 +674,4 @@ io-wq-check-that-the-predecessor-is-hashed-in-io_wq_remove_pending.patch
 net-rds-reset-op_nents-when-zerocopy-page-pin-fails.patch
 io_uring-prevent-opcode-speculation.patch
 s390-debug-reject-zero-length-input-before-trimming-.patch
+revert-x86-vdso-fix-output-operand-size-of-rdpid.patch
diff --git a/queue-6.12/revert-perf-cgroup-update-metric-leader-in-evlist__e.patch b/queue-6.12/revert-perf-cgroup-update-metric-leader-in-evlist__e.patch
new file mode 100644 (file)
index 0000000..e1dca8a
--- /dev/null
@@ -0,0 +1,79 @@
+From 5c9ceee75a90cd5db0d38d9dbfde6659194a9b5b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 24 May 2026 10:29:50 -0400
+Subject: Revert "perf cgroup: Update metric leader in evlist__expand_cgroup"
+
+This reverts commit d26e31446c0fa96feca0b7701243b42447225d33.
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/perf/util/cgroup.c | 30 +++++++-----------------------
+ 1 file changed, 7 insertions(+), 23 deletions(-)
+
+diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c
+index e172bcdf7fcb1..fbcc0626f9ce2 100644
+--- a/tools/perf/util/cgroup.c
++++ b/tools/perf/util/cgroup.c
+@@ -417,6 +417,7 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str,
+                         struct rblist *metric_events, bool open_cgroup)
+ {
+       struct evlist *orig_list, *tmp_list;
++      struct evsel *pos, *evsel, *leader;
+       struct rblist orig_metric_events;
+       struct cgroup *cgrp = NULL;
+       struct cgroup_name *cn;
+@@ -455,7 +456,6 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str,
+               goto out_err;
+       list_for_each_entry(cn, &cgroup_list, list) {
+-              struct evsel *pos;
+               char *name;
+               if (!cn->used)
+@@ -471,37 +471,21 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str,
+               if (cgrp == NULL)
+                       continue;
+-              /* copy the list and set to the new cgroup. */
++              leader = NULL;
+               evlist__for_each_entry(orig_list, pos) {
+-                      struct evsel *evsel = evsel__clone(/*dest=*/NULL, pos);
+-
++                      evsel = evsel__clone(/*dest=*/NULL, pos);
+                       if (evsel == NULL)
+                               goto out_err;
+-                      /* stash the copy during the copying. */
+-                      pos->priv = evsel;
+                       cgroup__put(evsel->cgrp);
+                       evsel->cgrp = cgroup__get(cgrp);
+-                      evlist__add(tmp_list, evsel);
+-              }
+-              /* update leader information using stashed pointer to copy. */
+-              evlist__for_each_entry(orig_list, pos) {
+-                      struct evsel *evsel = pos->priv;
+-
+-                      if (evsel__leader(pos))
+-                              evsel__set_leader(evsel, evsel__leader(pos)->priv);
+-
+-                      if (pos->metric_leader)
+-                              evsel->metric_leader = pos->metric_leader->priv;
++                      if (evsel__is_group_leader(pos))
++                              leader = evsel;
++                      evsel__set_leader(evsel, leader);
+-                      if (pos->first_wildcard_match)
+-                              evsel->first_wildcard_match = pos->first_wildcard_match->priv;
++                      evlist__add(tmp_list, evsel);
+               }
+-              /* the stashed copy is no longer used. */
+-              evlist__for_each_entry(orig_list, pos)
+-                      pos->priv = NULL;
+-
+               /* cgroup__new() has a refcount, release it here */
+               cgroup__put(cgrp);
+               nr_cgroups++;
+-- 
+2.53.0
+
diff --git a/queue-6.12/revert-perf-python-add-parse_events-function.patch b/queue-6.12/revert-perf-python-add-parse_events-function.patch
new file mode 100644 (file)
index 0000000..59f2242
--- /dev/null
@@ -0,0 +1,174 @@
+From 8a11c59a5aab9423777ee1ead5e66174896c7adb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 24 May 2026 10:36:48 -0400
+Subject: Revert "perf python: Add parse_events function"
+
+This reverts commit 9cd264079fab9867dbc9fbc8a1e521996e3d7212.
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/perf/util/cgroup.c |  2 +-
+ tools/perf/util/evsel.c  | 19 +++++--------
+ tools/perf/util/evsel.h  |  2 +-
+ tools/perf/util/python.c | 61 ----------------------------------------
+ 4 files changed, 9 insertions(+), 75 deletions(-)
+
+diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c
+index fbcc0626f9ce2..0f759dd96db71 100644
+--- a/tools/perf/util/cgroup.c
++++ b/tools/perf/util/cgroup.c
+@@ -473,7 +473,7 @@ int evlist__expand_cgroup(struct evlist *evlist, const char *str,
+               leader = NULL;
+               evlist__for_each_entry(orig_list, pos) {
+-                      evsel = evsel__clone(/*dest=*/NULL, pos);
++                      evsel = evsel__clone(pos);
+                       if (evsel == NULL)
+                               goto out_err;
+diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
+index 45a7ed5c7a473..d2965dc49bac2 100644
+--- a/tools/perf/util/evsel.c
++++ b/tools/perf/util/evsel.c
+@@ -332,7 +332,7 @@ static int evsel__copy_config_terms(struct evsel *dst, struct evsel *src)
+  * The assumption is that @orig is not configured nor opened yet.
+  * So we only care about the attributes that can be set while it's parsed.
+  */
+-struct evsel *evsel__clone(struct evsel *dest, struct evsel *orig)
++struct evsel *evsel__clone(struct evsel *orig)
+ {
+       struct evsel *evsel;
+@@ -345,11 +345,7 @@ struct evsel *evsel__clone(struct evsel *dest, struct evsel *orig)
+       if (orig->bpf_obj)
+               return NULL;
+-      if (dest)
+-              evsel = dest;
+-      else
+-              evsel = evsel__new(&orig->core.attr);
+-
++      evsel = evsel__new(&orig->core.attr);
+       if (evsel == NULL)
+               return NULL;
+@@ -399,12 +395,11 @@ struct evsel *evsel__clone(struct evsel *dest, struct evsel *orig)
+       evsel->core.leader = orig->core.leader;
+       evsel->max_events = orig->max_events;
+-      zfree(&evsel->unit);
+-      if (orig->unit) {
+-              evsel->unit = strdup(orig->unit);
+-              if (evsel->unit == NULL)
+-                      goto out_err;
+-      }
++      free((char *)evsel->unit);
++      evsel->unit = strdup(orig->unit);
++      if (evsel->unit == NULL)
++              goto out_err;
++
+       evsel->scale = orig->scale;
+       evsel->snapshot = orig->snapshot;
+       evsel->per_pkg = orig->per_pkg;
+diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
+index 0e64b9f17f0a6..b23fa3ca88883 100644
+--- a/tools/perf/util/evsel.h
++++ b/tools/perf/util/evsel.h
+@@ -241,7 +241,7 @@ static inline struct evsel *evsel__new(struct perf_event_attr *attr)
+       return evsel__new_idx(attr, 0);
+ }
+-struct evsel *evsel__clone(struct evsel *dest, struct evsel *orig);
++struct evsel *evsel__clone(struct evsel *orig);
+ int copy_config_terms(struct list_head *dst, struct list_head *src);
+ void free_config_terms(struct list_head *config_terms);
+diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
+index 5060dc801dede..e7f36ea9e2fa1 100644
+--- a/tools/perf/util/python.c
++++ b/tools/perf/util/python.c
+@@ -13,7 +13,6 @@
+ #include "evsel.h"
+ #include "event.h"
+ #include "print_binary.h"
+-#include "strbuf.h"
+ #include "thread_map.h"
+ #include "trace-event.h"
+ #include "mmap.h"
+@@ -1248,60 +1247,6 @@ static PyObject *pyrf__tracepoint(struct pyrf_evsel *pevsel,
+ #endif // HAVE_LIBTRACEEVENT
+ }
+-static PyObject *pyrf_evsel__from_evsel(struct evsel *evsel)
+-{
+-      struct pyrf_evsel *pevsel = PyObject_New(struct pyrf_evsel, &pyrf_evsel__type);
+-
+-      if (!pevsel)
+-              return NULL;
+-
+-      memset(&pevsel->evsel, 0, sizeof(pevsel->evsel));
+-      evsel__init(&pevsel->evsel, &evsel->core.attr, evsel->core.idx);
+-
+-      evsel__clone(&pevsel->evsel, evsel);
+-      return (PyObject *)pevsel;
+-}
+-
+-static PyObject *pyrf_evlist__from_evlist(struct evlist *evlist)
+-{
+-      struct pyrf_evlist *pevlist = PyObject_New(struct pyrf_evlist, &pyrf_evlist__type);
+-      struct evsel *pos;
+-
+-      if (!pevlist)
+-              return NULL;
+-
+-      memset(&pevlist->evlist, 0, sizeof(pevlist->evlist));
+-      evlist__init(&pevlist->evlist, evlist->core.all_cpus, evlist->core.threads);
+-      evlist__for_each_entry(evlist, pos) {
+-              struct pyrf_evsel *pevsel = (void *)pyrf_evsel__from_evsel(pos);
+-
+-              evlist__add(&pevlist->evlist, &pevsel->evsel);
+-      }
+-      return (PyObject *)pevlist;
+-}
+-
+-static PyObject *pyrf__parse_events(PyObject *self, PyObject *args)
+-{
+-      const char *input;
+-      struct evlist evlist = {};
+-      struct parse_events_error err;
+-      PyObject *result;
+-
+-      if (!PyArg_ParseTuple(args, "s", &input))
+-              return NULL;
+-
+-      parse_events_error__init(&err);
+-      evlist__init(&evlist, NULL, NULL);
+-      if (parse_events(&evlist, input, &err)) {
+-              parse_events_error__print(&err, input);
+-              PyErr_SetFromErrno(PyExc_OSError);
+-              return NULL;
+-      }
+-      result = pyrf_evlist__from_evlist(&evlist);
+-      evlist__exit(&evlist);
+-      return result;
+-}
+-
+ static PyMethodDef perf__methods[] = {
+       {
+               .ml_name  = "tracepoint",
+@@ -1309,12 +1254,6 @@ static PyMethodDef perf__methods[] = {
+               .ml_flags = METH_VARARGS | METH_KEYWORDS,
+               .ml_doc   = PyDoc_STR("Get tracepoint config.")
+       },
+-      {
+-              .ml_name  = "parse_events",
+-              .ml_meth  = (PyCFunction) pyrf__parse_events,
+-              .ml_flags = METH_VARARGS,
+-              .ml_doc   = PyDoc_STR("Parse a string of events and return an evlist.")
+-      },
+       { .ml_name = NULL, }
+ };
+-- 
+2.53.0
+
diff --git a/queue-6.12/revert-perf-tool_pmu-factor-tool-events-into-their-o.patch b/queue-6.12/revert-perf-tool_pmu-factor-tool-events-into-their-o.patch
new file mode 100644 (file)
index 0000000..6d2806f
--- /dev/null
@@ -0,0 +1,1430 @@
+From 0a56a56da306fbc202f01e7373295d6239716451 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 24 May 2026 10:36:48 -0400
+Subject: Revert "perf tool_pmu: Factor tool events into their own PMU"
+
+This reverts commit 7cfcd01f33fc3400c60f923d2896a8cdc60cecc4.
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/perf/builtin-list.c      |  13 +-
+ tools/perf/builtin-stat.c      |   1 -
+ tools/perf/util/Build          |   1 -
+ tools/perf/util/evsel.c        | 272 ++++++++++++++++++++--
+ tools/perf/util/evsel.h        |  28 ++-
+ tools/perf/util/metricgroup.c  |   1 -
+ tools/perf/util/parse-events.c |  39 ++++
+ tools/perf/util/parse-events.h |   3 +
+ tools/perf/util/parse-events.l |  11 +
+ tools/perf/util/parse-events.y |  16 ++
+ tools/perf/util/pmu.c          |  20 +-
+ tools/perf/util/pmu.h          |   2 -
+ tools/perf/util/pmus.c         |   9 -
+ tools/perf/util/print-events.c |  36 ++-
+ tools/perf/util/print-events.h |   1 +
+ tools/perf/util/stat-display.c |   6 +-
+ tools/perf/util/stat-shadow.c  |   1 -
+ tools/perf/util/tool_pmu.c     | 411 ---------------------------------
+ tools/perf/util/tool_pmu.h     |  51 ----
+ 19 files changed, 392 insertions(+), 530 deletions(-)
+ delete mode 100644 tools/perf/util/tool_pmu.c
+ delete mode 100644 tools/perf/util/tool_pmu.h
+
+diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
+index 9e7fdfcdd7ffb..c5331721dfee9 100644
+--- a/tools/perf/builtin-list.c
++++ b/tools/perf/builtin-list.c
+@@ -19,7 +19,6 @@
+ #include "util/string2.h"
+ #include "util/strlist.h"
+ #include "util/strbuf.h"
+-#include "util/tool_pmu.h"
+ #include <subcmd/pager.h>
+ #include <subcmd/parse-options.h>
+ #include <linux/zalloc.h>
+@@ -615,18 +614,9 @@ int cmd_list(int argc, const char **argv)
+                                       event_symbols_hw, PERF_COUNT_HW_MAX);
+               else if (strcmp(argv[i], "sw") == 0 ||
+                        strcmp(argv[i], "software") == 0) {
+-                      char *old_pmu_glob = default_ps.pmu_glob;
+-
+                       print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE,
+                                       event_symbols_sw, PERF_COUNT_SW_MAX);
+-                      default_ps.pmu_glob = strdup("tool");
+-                      if (!default_ps.pmu_glob) {
+-                              ret = -1;
+-                              goto out;
+-                      }
+-                      perf_pmus__print_pmu_events(&print_cb, ps);
+-                      zfree(&default_ps.pmu_glob);
+-                      default_ps.pmu_glob = old_pmu_glob;
++                      print_tool_events(&print_cb, ps);
+               } else if (strcmp(argv[i], "cache") == 0 ||
+                        strcmp(argv[i], "hwcache") == 0)
+                       print_hwcache_events(&print_cb, ps);
+@@ -674,6 +664,7 @@ int cmd_list(int argc, const char **argv)
+                                       event_symbols_hw, PERF_COUNT_HW_MAX);
+                       print_symbol_events(&print_cb, ps, PERF_TYPE_SOFTWARE,
+                                       event_symbols_sw, PERF_COUNT_SW_MAX);
++                      print_tool_events(&print_cb, ps);
+                       print_hwcache_events(&print_cb, ps);
+                       perf_pmus__print_pmu_events(&print_cb, ps);
+                       print_tracepoint_events(&print_cb, ps);
+diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
+index e8708f785e7f2..e476598de8083 100644
+--- a/tools/perf/builtin-stat.c
++++ b/tools/perf/builtin-stat.c
+@@ -46,7 +46,6 @@
+ #include "util/parse-events.h"
+ #include "util/pmus.h"
+ #include "util/pmu.h"
+-#include "util/tool_pmu.h"
+ #include "util/event.h"
+ #include "util/evlist.h"
+ #include "util/evsel.h"
+diff --git a/tools/perf/util/Build b/tools/perf/util/Build
+index fa508e113dd0c..dc616292b2ddf 100644
+--- a/tools/perf/util/Build
++++ b/tools/perf/util/Build
+@@ -83,7 +83,6 @@ perf-util-y += pmu.o
+ perf-util-y += pmus.o
+ perf-util-y += pmu-flex.o
+ perf-util-y += pmu-bison.o
+-perf-util-y += tool_pmu.o
+ perf-util-y += svghelper.o
+ perf-util-$(CONFIG_LIBTRACEEVENT) += trace-event-info.o
+ perf-util-y += trace-event-scripting.o
+diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
+index d2965dc49bac2..6e8d70ec05bad 100644
+--- a/tools/perf/util/evsel.c
++++ b/tools/perf/util/evsel.c
+@@ -10,6 +10,7 @@
+ #include <errno.h>
+ #include <inttypes.h>
+ #include <linux/bitops.h>
++#include <api/io.h>
+ #include <api/fs/fs.h>
+ #include <api/fs/tracing_path.h>
+ #include <linux/hw_breakpoint.h>
+@@ -50,7 +51,6 @@
+ #include "off_cpu.h"
+ #include "pmu.h"
+ #include "pmus.h"
+-#include "tool_pmu.h"
+ #include "rlimit.h"
+ #include "../perf-sys.h"
+ #include "util/parse-branch-options.h"
+@@ -71,6 +71,33 @@ struct perf_missing_features perf_missing_features;
+ static clockid_t clockid;
++static const char *const perf_tool_event__tool_names[PERF_TOOL_MAX] = {
++      NULL,
++      "duration_time",
++      "user_time",
++      "system_time",
++};
++
++const char *perf_tool_event__to_str(enum perf_tool_event ev)
++{
++      if (ev > PERF_TOOL_NONE && ev < PERF_TOOL_MAX)
++              return perf_tool_event__tool_names[ev];
++
++      return NULL;
++}
++
++enum perf_tool_event perf_tool_event__from_str(const char *str)
++{
++      int i;
++
++      perf_tool_event__for_each_event(i) {
++              if (!strcmp(str, perf_tool_event__tool_names[i]))
++                      return i;
++      }
++      return PERF_TOOL_NONE;
++}
++
++
+ static int evsel__no_extra_init(struct evsel *evsel __maybe_unused)
+ {
+       return 0;
+@@ -395,6 +422,7 @@ struct evsel *evsel__clone(struct evsel *orig)
+       evsel->core.leader = orig->core.leader;
+       evsel->max_events = orig->max_events;
++      evsel->tool_event = orig->tool_event;
+       free((char *)evsel->unit);
+       evsel->unit = strdup(orig->unit);
+       if (evsel->unit == NULL)
+@@ -592,6 +620,11 @@ static int evsel__sw_name(struct evsel *evsel, char *bf, size_t size)
+       return r + evsel__add_modifiers(evsel, bf + r, size - r);
+ }
++static int evsel__tool_name(enum perf_tool_event ev, char *bf, size_t size)
++{
++      return scnprintf(bf, size, "%s", perf_tool_event__to_str(ev));
++}
++
+ static int __evsel__bp_name(char *bf, size_t size, u64 addr, u64 type)
+ {
+       int r;
+@@ -742,7 +775,10 @@ const char *evsel__name(struct evsel *evsel)
+               break;
+       case PERF_TYPE_SOFTWARE:
+-              evsel__sw_name(evsel, bf, sizeof(bf));
++              if (evsel__is_tool(evsel))
++                      evsel__tool_name(evsel__tool_event(evsel), bf, sizeof(bf));
++              else
++                      evsel__sw_name(evsel, bf, sizeof(bf));
+               break;
+       case PERF_TYPE_TRACEPOINT:
+@@ -753,10 +789,6 @@ const char *evsel__name(struct evsel *evsel)
+               evsel__bp_name(evsel, bf, sizeof(bf));
+               break;
+-      case PERF_PMU_TYPE_TOOL:
+-              scnprintf(bf, sizeof(bf), "%s", evsel__tool_pmu_event_name(evsel));
+-              break;
+-
+       default:
+               scnprintf(bf, sizeof(bf), "unknown attr type: %d",
+                         evsel->core.attr.type);
+@@ -782,7 +814,7 @@ const char *evsel__metric_id(const struct evsel *evsel)
+               return evsel->metric_id;
+       if (evsel__is_tool(evsel))
+-              return evsel__tool_pmu_event_name(evsel);
++              return perf_tool_event__to_str(evsel__tool_event(evsel));
+       return "unknown";
+ }
+@@ -1666,6 +1698,167 @@ static int evsel__read_group(struct evsel *leader, int cpu_map_idx, int thread)
+       return evsel__process_group_data(leader, cpu_map_idx, thread, data);
+ }
++static bool read_until_char(struct io *io, char e)
++{
++      int c;
++
++      do {
++              c = io__get_char(io);
++              if (c == -1)
++                      return false;
++      } while (c != e);
++      return true;
++}
++
++static int read_stat_field(int fd, struct perf_cpu cpu, int field, __u64 *val)
++{
++      char buf[256];
++      struct io io;
++      int i;
++
++      io__init(&io, fd, buf, sizeof(buf));
++
++      /* Skip lines to relevant CPU. */
++      for (i = -1; i < cpu.cpu; i++) {
++              if (!read_until_char(&io, '\n'))
++                      return -EINVAL;
++      }
++      /* Skip to "cpu". */
++      if (io__get_char(&io) != 'c') return -EINVAL;
++      if (io__get_char(&io) != 'p') return -EINVAL;
++      if (io__get_char(&io) != 'u') return -EINVAL;
++
++      /* Skip N of cpuN. */
++      if (!read_until_char(&io, ' '))
++              return -EINVAL;
++
++      i = 1;
++      while (true) {
++              if (io__get_dec(&io, val) != ' ')
++                      break;
++              if (field == i)
++                      return 0;
++              i++;
++      }
++      return -EINVAL;
++}
++
++static int read_pid_stat_field(int fd, int field, __u64 *val)
++{
++      char buf[256];
++      struct io io;
++      int c, i;
++
++      io__init(&io, fd, buf, sizeof(buf));
++      if (io__get_dec(&io, val) != ' ')
++              return -EINVAL;
++      if (field == 1)
++              return 0;
++
++      /* Skip comm. */
++      if (io__get_char(&io) != '(' || !read_until_char(&io, ')'))
++              return -EINVAL;
++      if (field == 2)
++              return -EINVAL; /* String can't be returned. */
++
++      /* Skip state */
++      if (io__get_char(&io) != ' ' || io__get_char(&io) == -1)
++              return -EINVAL;
++      if (field == 3)
++              return -EINVAL; /* String can't be returned. */
++
++      /* Loop over numeric fields*/
++      if (io__get_char(&io) != ' ')
++              return -EINVAL;
++
++      i = 4;
++      while (true) {
++              c = io__get_dec(&io, val);
++              if (c == -1)
++                      return -EINVAL;
++              if (c == -2) {
++                      /* Assume a -ve was read */
++                      c = io__get_dec(&io, val);
++                      *val *= -1;
++              }
++              if (c != ' ')
++                      return -EINVAL;
++              if (field == i)
++                      return 0;
++              i++;
++      }
++      return -EINVAL;
++}
++
++static int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread)
++{
++      __u64 *start_time, cur_time, delta_start;
++      int fd, err = 0;
++      struct perf_counts_values *count;
++      bool adjust = false;
++
++      count = perf_counts(evsel->counts, cpu_map_idx, thread);
++
++      switch (evsel__tool_event(evsel)) {
++      case PERF_TOOL_DURATION_TIME:
++              /*
++               * Pretend duration_time is only on the first CPU and thread, or
++               * else aggregation will scale duration_time by the number of
++               * CPUs/threads.
++               */
++              start_time = &evsel->start_time;
++              if (cpu_map_idx == 0 && thread == 0)
++                      cur_time = rdclock();
++              else
++                      cur_time = *start_time;
++              break;
++      case PERF_TOOL_USER_TIME:
++      case PERF_TOOL_SYSTEM_TIME: {
++              bool system = evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME;
++
++              start_time = xyarray__entry(evsel->start_times, cpu_map_idx, thread);
++              fd = FD(evsel, cpu_map_idx, thread);
++              lseek(fd, SEEK_SET, 0);
++              if (evsel->pid_stat) {
++                      /* The event exists solely on 1 CPU. */
++                      if (cpu_map_idx == 0)
++                              err = read_pid_stat_field(fd, system ? 15 : 14, &cur_time);
++                      else
++                              cur_time = 0;
++              } else {
++                      /* The event is for all threads. */
++                      if (thread == 0) {
++                              struct perf_cpu cpu = perf_cpu_map__cpu(evsel->core.cpus,
++                                                                      cpu_map_idx);
++
++                              err = read_stat_field(fd, cpu, system ? 3 : 1, &cur_time);
++                      } else {
++                              cur_time = 0;
++                      }
++              }
++              adjust = true;
++              break;
++      }
++      case PERF_TOOL_NONE:
++      case PERF_TOOL_MAX:
++      default:
++              err = -EINVAL;
++      }
++      if (err)
++              return err;
++
++      delta_start = cur_time - *start_time;
++      if (adjust) {
++              __u64 ticks_per_sec = sysconf(_SC_CLK_TCK);
++
++              delta_start *= 1000000000 / ticks_per_sec;
++      }
++      count->val    = delta_start;
++      count->ena    = count->run = delta_start;
++      count->lost   = 0;
++      return 0;
++}
++
+ bool __evsel__match(const struct evsel *evsel, u32 type, u64 config)
+ {
+@@ -1881,7 +2074,6 @@ static struct perf_thread_map *empty_thread_map;
+ static int __evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus,
+               struct perf_thread_map *threads)
+ {
+-      int ret = 0;
+       int nthreads = perf_thread_map__nr(threads);
+       if ((perf_missing_features.write_backward && evsel->core.attr.write_backward) ||
+@@ -1912,14 +2104,19 @@ static int __evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus,
+           perf_evsel__alloc_fd(&evsel->core, perf_cpu_map__nr(cpus), nthreads) < 0)
+               return -ENOMEM;
+-      if (evsel__is_tool(evsel))
+-              ret = evsel__tool_pmu_prepare_open(evsel, cpus, nthreads);
++      if ((evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME ||
++           evsel__tool_event(evsel) == PERF_TOOL_USER_TIME) &&
++          !evsel->start_times) {
++              evsel->start_times = xyarray__new(perf_cpu_map__nr(cpus), nthreads, sizeof(__u64));
++              if (!evsel->start_times)
++                      return -ENOMEM;
++      }
+       evsel->open_flags = PERF_FLAG_FD_CLOEXEC;
+       if (evsel->cgrp)
+               evsel->open_flags |= PERF_FLAG_PID_CGROUP;
+-      return ret;
++      return 0;
+ }
+ static void evsel__disable_missing_features(struct evsel *evsel)
+@@ -2097,6 +2294,13 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,
+       int pid = -1, err, old_errno;
+       enum rlimit_action set_rlimit = NO_CHANGE;
++      if (evsel__tool_event(evsel) == PERF_TOOL_DURATION_TIME) {
++              if (evsel->core.attr.sample_period) /* no sampling */
++                      return -EINVAL;
++              evsel->start_time = rdclock();
++              return 0;
++      }
++
+       if (evsel__is_retire_lat(evsel))
+               return tpebs_start(evsel->evlist);
+@@ -2121,12 +2325,6 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,
+       pr_debug3("Opening: %s\n", evsel__name(evsel));
+       display_attr(&evsel->core.attr);
+-      if (evsel__is_tool(evsel)) {
+-              return evsel__tool_pmu_open(evsel, threads,
+-                                          start_cpu_map_idx,
+-                                          end_cpu_map_idx);
+-      }
+-
+       for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) {
+               for (thread = 0; thread < nthreads; thread++) {
+@@ -2138,6 +2336,46 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,
+                       if (!evsel->cgrp && !evsel->core.system_wide)
+                               pid = perf_thread_map__pid(threads, thread);
++                      if (evsel__tool_event(evsel) == PERF_TOOL_USER_TIME ||
++                          evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME) {
++                              bool system = evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME;
++                              __u64 *start_time = NULL;
++
++                              if (evsel->core.attr.sample_period) {
++                                      /* no sampling */
++                                      err = -EINVAL;
++                                      goto out_close;
++                              }
++                              if (pid > -1) {
++                                      char buf[64];
++
++                                      snprintf(buf, sizeof(buf), "/proc/%d/stat", pid);
++                                      fd = open(buf, O_RDONLY);
++                                      evsel->pid_stat = true;
++                              } else {
++                                      fd = open("/proc/stat", O_RDONLY);
++                              }
++                              FD(evsel, idx, thread) = fd;
++                              if (fd < 0) {
++                                      err = -errno;
++                                      goto out_close;
++                              }
++                              start_time = xyarray__entry(evsel->start_times, idx, thread);
++                              if (pid > -1) {
++                                      err = read_pid_stat_field(fd, system ? 15 : 14,
++                                                                start_time);
++                              } else {
++                                      struct perf_cpu cpu;
++
++                                      cpu = perf_cpu_map__cpu(evsel->core.cpus, idx);
++                                      err = read_stat_field(fd, cpu, system ? 3 : 1,
++                                                            start_time);
++                              }
++                              if (err)
++                                      goto out_close;
++                              continue;
++                      }
++
+                       group_fd = get_group_fd(evsel, idx, thread);
+                       if (group_fd == -2) {
+diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
+index b23fa3ca88883..dc0d300776f16 100644
+--- a/tools/perf/util/evsel.h
++++ b/tools/perf/util/evsel.h
+@@ -11,7 +11,6 @@
+ #include <perf/evsel.h>
+ #include "symbol_conf.h"
+ #include "pmus.h"
+-#include "pmu.h"
+ struct bpf_object;
+ struct cgroup;
+@@ -23,9 +22,25 @@ struct target;
+ struct hashmap;
+ struct bperf_leader_bpf;
+ struct bperf_follower_bpf;
++struct perf_pmu;
+ typedef int (evsel__sb_cb_t)(union perf_event *event, void *data);
++enum perf_tool_event {
++      PERF_TOOL_NONE          = 0,
++      PERF_TOOL_DURATION_TIME = 1,
++      PERF_TOOL_USER_TIME = 2,
++      PERF_TOOL_SYSTEM_TIME = 3,
++
++      PERF_TOOL_MAX,
++};
++
++const char *perf_tool_event__to_str(enum perf_tool_event ev);
++enum perf_tool_event perf_tool_event__from_str(const char *str);
++
++#define perf_tool_event__for_each_event(ev)           \
++      for ((ev) = PERF_TOOL_DURATION_TIME; (ev) < PERF_TOOL_MAX; ev++)
++
+ /** struct evsel - event selector
+  *
+  * @evlist - evlist this evsel is in, if it is in one.
+@@ -68,6 +83,7 @@ struct evsel {
+               const char              *unit;
+               struct cgroup           *cgrp;
+               const char              *metric_id;
++              enum perf_tool_event    tool_event;
+               /* parse modifier helper */
+               int                     exclude_GH;
+               int                     sample_read;
+@@ -307,11 +323,21 @@ const char *evsel__name(struct evsel *evsel);
+ bool evsel__name_is(struct evsel *evsel, const char *name);
+ const char *evsel__metric_id(const struct evsel *evsel);
++static inline bool evsel__is_tool(const struct evsel *evsel)
++{
++      return evsel->tool_event != PERF_TOOL_NONE;
++}
++
+ static inline bool evsel__is_retire_lat(const struct evsel *evsel)
+ {
+       return evsel->retire_lat;
+ }
++static inline enum perf_tool_event evsel__tool_event(const struct evsel *evsel)
++{
++      return evsel->tool_event;
++}
++
+ const char *evsel__group_name(struct evsel *evsel);
+ int evsel__group_desc(struct evsel *evsel, char *buf, size_t size);
+diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
+index 9181548e88810..4dff3e925a47b 100644
+--- a/tools/perf/util/metricgroup.c
++++ b/tools/perf/util/metricgroup.c
+@@ -14,7 +14,6 @@
+ #include "pmus.h"
+ #include "print-events.h"
+ #include "smt.h"
+-#include "tool_pmu.h"
+ #include "expr.h"
+ #include "rblist.h"
+ #include <string.h>
+diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
+index ba84a451c70a3..fcc4dab618bee 100644
+--- a/tools/perf/util/parse-events.c
++++ b/tools/perf/util/parse-events.c
+@@ -302,6 +302,38 @@ static int add_event(struct list_head *list, int *idx,
+                          alternate_hw_config) ? 0 : -ENOMEM;
+ }
++static int add_event_tool(struct list_head *list, int *idx,
++                        enum perf_tool_event tool_event)
++{
++      struct evsel *evsel;
++      struct perf_event_attr attr = {
++              .type = PERF_TYPE_SOFTWARE,
++              .config = PERF_COUNT_SW_DUMMY,
++      };
++      struct perf_cpu_map *cpu_list = NULL;
++
++      if (tool_event == PERF_TOOL_DURATION_TIME) {
++              /* Duration time is gathered globally, pretend it is only on CPU0. */
++              cpu_list = perf_cpu_map__new("0");
++      }
++      evsel = __add_event(list, idx, &attr, /*init_attr=*/true, /*name=*/NULL,
++                          /*metric_id=*/NULL, /*pmu=*/NULL,
++                          /*config_terms=*/NULL, /*auto_merge_stats=*/false,
++                          cpu_list,
++                          /*alternate_hw_config=*/PERF_COUNT_HW_MAX);
++      perf_cpu_map__put(cpu_list);
++      if (!evsel)
++              return -ENOMEM;
++      evsel->tool_event = tool_event;
++      if (tool_event == PERF_TOOL_DURATION_TIME
++          || tool_event == PERF_TOOL_USER_TIME
++          || tool_event == PERF_TOOL_SYSTEM_TIME) {
++              free((char *)evsel->unit);
++              evsel->unit = strdup("ns");
++      }
++      return 0;
++}
++
+ /**
+  * parse_aliases - search names for entries beginning or equalling str ignoring
+  *                 case. If mutliple entries in names match str then the longest
+@@ -1398,6 +1430,13 @@ int parse_events_add_numeric(struct parse_events_state *parse_state,
+                                       type, /*extended_type=*/0, config, head_config);
+ }
++int parse_events_add_tool(struct parse_events_state *parse_state,
++                        struct list_head *list,
++                        int tool_event)
++{
++      return add_event_tool(list, &parse_state->idx, tool_event);
++}
++
+ static bool config_term_percore(struct list_head *config_terms)
+ {
+       struct evsel_config_term *term;
+diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
+index e9f59de2304be..2b52f8d6aa29a 100644
+--- a/tools/perf/util/parse-events.h
++++ b/tools/perf/util/parse-events.h
+@@ -227,6 +227,9 @@ int parse_events_add_numeric(struct parse_events_state *parse_state,
+                            u32 type, u64 config,
+                            const struct parse_events_terms *head_config,
+                            bool wildcard);
++int parse_events_add_tool(struct parse_events_state *parse_state,
++                        struct list_head *list,
++                        int tool_event);
+ int parse_events_add_cache(struct list_head *list, int *idx, const char *name,
+                          struct parse_events_state *parse_state,
+                          struct parse_events_terms *parsed_terms);
+diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
+index 14e5bd856a187..5a0bcd7f166ae 100644
+--- a/tools/perf/util/parse-events.l
++++ b/tools/perf/util/parse-events.l
+@@ -121,6 +121,14 @@ static int sym(yyscan_t scanner, int type, int config)
+       return type == PERF_TYPE_HARDWARE ? PE_VALUE_SYM_HW : PE_VALUE_SYM_SW;
+ }
++static int tool(yyscan_t scanner, enum perf_tool_event event)
++{
++      YYSTYPE *yylval = parse_events_get_lval(scanner);
++
++      yylval->num = event;
++      return PE_VALUE_SYM_TOOL;
++}
++
+ static int term(yyscan_t scanner, enum parse_events__term_type type)
+ {
+       YYSTYPE *yylval = parse_events_get_lval(scanner);
+@@ -396,6 +404,9 @@ cpu-migrations|migrations                  { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COU
+ alignment-faults                              { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); }
+ emulation-faults                              { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
+ dummy                                         { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); }
++duration_time                                 { return tool(yyscanner, PERF_TOOL_DURATION_TIME); }
++user_time                                             { return tool(yyscanner, PERF_TOOL_USER_TIME); }
++system_time                                           { return tool(yyscanner, PERF_TOOL_SYSTEM_TIME); }
+ bpf-output                                    { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_BPF_OUTPUT); }
+ cgroup-switches                                       { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CGROUP_SWITCHES); }
+diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
+index f888cbb076d67..dcf47fabdfdd7 100644
+--- a/tools/perf/util/parse-events.y
++++ b/tools/perf/util/parse-events.y
+@@ -56,6 +56,7 @@ static void free_list_evsel(struct list_head* list_evsel)
+ %token PE_START_EVENTS PE_START_TERMS
+ %token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_TERM
++%token PE_VALUE_SYM_TOOL
+ %token PE_EVENT_NAME
+ %token PE_RAW PE_NAME
+ %token PE_MODIFIER_EVENT PE_MODIFIER_BP PE_BP_COLON PE_BP_SLASH
+@@ -67,6 +68,7 @@ static void free_list_evsel(struct list_head* list_evsel)
+ %type <num> PE_VALUE
+ %type <num> PE_VALUE_SYM_HW
+ %type <num> PE_VALUE_SYM_SW
++%type <num> PE_VALUE_SYM_TOOL
+ %type <mod> PE_MODIFIER_EVENT
+ %type <term_type> PE_TERM
+ %type <num> value_sym
+@@ -348,6 +350,20 @@ value_sym sep_slash_slash_dc
+               PE_ABORT(err);
+       $$ = list;
+ }
++|
++PE_VALUE_SYM_TOOL sep_slash_slash_dc
++{
++      struct list_head *list;
++      int err;
++
++      list = alloc_list();
++      if (!list)
++              YYNOMEM;
++      err = parse_events_add_tool(_parse_state, list, $1);
++      if (err)
++              YYNOMEM;
++      $$ = list;
++}
+ event_legacy_cache:
+ PE_LEGACY_CACHE opt_event_config
+diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
+index 15fb144e890f0..8885998c19530 100644
+--- a/tools/perf/util/pmu.c
++++ b/tools/perf/util/pmu.c
+@@ -19,7 +19,6 @@
+ #include "evsel.h"
+ #include "pmu.h"
+ #include "pmus.h"
+-#include "tool_pmu.h"
+ #include <util/pmu-bison.h>
+ #include <util/pmu-flex.h>
+ #include "parse-events.h"
+@@ -1512,9 +1511,6 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
+ {
+       bool zero = !!pmu->perf_event_attr_init_default;
+-      if (perf_pmu__is_tool(pmu))
+-              return tool_pmu__config_terms(attr, head_terms, err);
+-
+       /* Fake PMU doesn't have proper terms so nothing to configure in attr. */
+       if (perf_pmu__is_fake(pmu))
+               return 0;
+@@ -1627,8 +1623,8 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct parse_events_terms *head_
+       info->scale    = 0.0;
+       info->snapshot = false;
+-      /* Tool/fake PMU doesn't rewrite terms. */
+-      if (perf_pmu__is_tool(pmu) || perf_pmu__is_fake(pmu))
++      /* Fake PMU doesn't rewrite terms. */
++      if (perf_pmu__is_fake(pmu))
+               goto out;
+       list_for_each_entry_safe(term, h, &head_terms->terms, list) {
+@@ -1798,8 +1794,6 @@ bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name)
+ {
+       if (!name)
+               return false;
+-      if (perf_pmu__is_tool(pmu))
+-              return perf_tool_event__from_str(name) != PERF_TOOL_NONE;
+       if (perf_pmu__find_alias(pmu, name, /*load=*/ true) != NULL)
+               return true;
+       if (pmu->cpu_aliases_added || !pmu->events_table)
+@@ -1811,9 +1805,6 @@ size_t perf_pmu__num_events(struct perf_pmu *pmu)
+ {
+       size_t nr;
+-      if (perf_pmu__is_tool(pmu))
+-              return tool_pmu__num_events();
+-
+       pmu_aliases_parse(pmu);
+       nr = pmu->sysfs_aliases + pmu->sys_json_aliases;
+@@ -1875,9 +1866,6 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus,
+       int ret = 0;
+       struct strbuf sb;
+-      if (perf_pmu__is_tool(pmu))
+-              return tool_pmu__for_each_event_cb(pmu, state, cb);
+-
+       strbuf_init(&sb, /*hint=*/ 0);
+       pmu_aliases_parse(pmu);
+       pmu_add_cpu_aliases(pmu);
+@@ -1966,7 +1954,6 @@ bool perf_pmu__is_software(const struct perf_pmu *pmu)
+       case PERF_TYPE_HW_CACHE:        return false;
+       case PERF_TYPE_RAW:             return false;
+       case PERF_TYPE_BREAKPOINT:      return true;
+-      case PERF_PMU_TYPE_TOOL:        return true;
+       default: break;
+       }
+       for (size_t i = 0; i < ARRAY_SIZE(known_sw_pmus); i++) {
+@@ -2294,9 +2281,6 @@ const char *perf_pmu__name_from_config(struct perf_pmu *pmu, u64 config)
+       if (!pmu)
+               return NULL;
+-      if (perf_pmu__is_tool(pmu))
+-              return perf_tool_event__to_str(config);
+-
+       pmu_aliases_parse(pmu);
+       pmu_add_cpu_aliases(pmu);
+       list_for_each_entry(event, &pmu->aliases, list) {
+diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
+index 2dba1cfa20ddd..0222124b86b92 100644
+--- a/tools/perf/util/pmu.h
++++ b/tools/perf/util/pmu.h
+@@ -37,7 +37,6 @@ struct perf_pmu_caps {
+ };
+ enum {
+-      PERF_PMU_TYPE_TOOL = 0xFFFFFFFE,
+       PERF_PMU_TYPE_FAKE = 0xFFFFFFFF,
+ };
+@@ -286,7 +285,6 @@ struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char
+ struct perf_pmu *perf_pmu__create_placeholder_core_pmu(struct list_head *core_pmus);
+ void perf_pmu__delete(struct perf_pmu *pmu);
+ struct perf_pmu *perf_pmus__find_core_pmu(void);
+-
+ const char *perf_pmu__name_from_config(struct perf_pmu *pmu, u64 config);
+ #endif /* __PMU_H */
+diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
+index 5af26a08fb915..362596ed27294 100644
+--- a/tools/perf/util/pmus.c
++++ b/tools/perf/util/pmus.c
+@@ -15,7 +15,6 @@
+ #include "evsel.h"
+ #include "pmus.h"
+ #include "pmu.h"
+-#include "tool_pmu.h"
+ #include "print-events.h"
+ #include "strbuf.h"
+@@ -201,7 +200,6 @@ static void pmu_read_sysfs(bool core_only)
+       int fd;
+       DIR *dir;
+       struct dirent *dent;
+-      struct perf_pmu *tool_pmu;
+       if (read_sysfs_all_pmus || (core_only && read_sysfs_core_pmus))
+               return;
+@@ -231,10 +229,6 @@ static void pmu_read_sysfs(bool core_only)
+                       pr_err("Failure to set up any core PMUs\n");
+       }
+       list_sort(NULL, &core_pmus, pmus_cmp);
+-      if (!core_only) {
+-              tool_pmu = perf_pmus__tool_pmu();
+-              list_add_tail(&tool_pmu->list, &other_pmus);
+-      }
+       list_sort(NULL, &other_pmus, pmus_cmp);
+       if (!list_empty(&core_pmus)) {
+               read_sysfs_core_pmus = true;
+@@ -590,9 +584,6 @@ void perf_pmus__print_raw_pmu_events(const struct print_callbacks *print_cb, voi
+               int len = pmu_name_len_no_suffix(pmu->name);
+               const char *desc = "(see 'man perf-list' or 'man perf-record' on how to encode it)";
+-              if (perf_pmu__is_tool(pmu))
+-                      continue;
+-
+               if (!pmu->is_core)
+                       desc = NULL;
+diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c
+index 83aaf7cda6359..a1c71d9793bd8 100644
+--- a/tools/perf/util/print-events.c
++++ b/tools/perf/util/print-events.c
+@@ -29,7 +29,6 @@
+ #include "tracepoint.h"
+ #include "pfm.h"
+ #include "thread_map.h"
+-#include "tool_pmu.h"
+ #include "util.h"
+ #define MAX_NAME_LEN 100
+@@ -44,6 +43,21 @@ static const char * const event_type_descriptors[] = {
+       "Hardware breakpoint",
+ };
++static const struct event_symbol event_symbols_tool[PERF_TOOL_MAX] = {
++      [PERF_TOOL_DURATION_TIME] = {
++              .symbol = "duration_time",
++              .alias  = "",
++      },
++      [PERF_TOOL_USER_TIME] = {
++              .symbol = "user_time",
++              .alias  = "",
++      },
++      [PERF_TOOL_SYSTEM_TIME] = {
++              .symbol = "system_time",
++              .alias  = "",
++      },
++};
++
+ /*
+  * Print the events from <debugfs_mount_point>/tracing/events
+  */
+@@ -328,6 +342,24 @@ int print_hwcache_events(const struct print_callbacks *print_cb, void *print_sta
+       return 0;
+ }
++void print_tool_events(const struct print_callbacks *print_cb, void *print_state)
++{
++      // Start at 1 because the first enum entry means no tool event.
++      for (int i = 1; i < PERF_TOOL_MAX; ++i) {
++              print_cb->print_event(print_state,
++                              "tool",
++                              /*pmu_name=*/NULL,
++                              event_symbols_tool[i].symbol,
++                              event_symbols_tool[i].alias,
++                              /*scale_unit=*/NULL,
++                              /*deprecated=*/false,
++                              "Tool event",
++                              /*desc=*/NULL,
++                              /*long_desc=*/NULL,
++                              /*encoding_desc=*/NULL);
++      }
++}
++
+ void print_symbol_events(const struct print_callbacks *print_cb, void *print_state,
+                        unsigned int type, const struct event_symbol *syms,
+                        unsigned int max)
+@@ -391,6 +423,8 @@ void print_events(const struct print_callbacks *print_cb, void *print_state)
+       print_symbol_events(print_cb, print_state, PERF_TYPE_SOFTWARE,
+                       event_symbols_sw, PERF_COUNT_SW_MAX);
++      print_tool_events(print_cb, print_state);
++
+       print_hwcache_events(print_cb, print_state);
+       perf_pmus__print_pmu_events(print_cb, print_state);
+diff --git a/tools/perf/util/print-events.h b/tools/perf/util/print-events.h
+index 445efa1636c1b..bf4290bef0cd6 100644
+--- a/tools/perf/util/print-events.h
++++ b/tools/perf/util/print-events.h
+@@ -36,6 +36,7 @@ void print_sdt_events(const struct print_callbacks *print_cb, void *print_state)
+ void print_symbol_events(const struct print_callbacks *print_cb, void *print_state,
+                        unsigned int type, const struct event_symbol *syms,
+                        unsigned int max);
++void print_tool_events(const struct print_callbacks *print_cb, void *print_state);
+ void print_tracepoint_events(const struct print_callbacks *print_cb, void *print_state);
+ bool is_event_supported(u8 type, u64 config);
+diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
+index a82a8ec79b399..ea96e4ebad8c8 100644
+--- a/tools/perf/util/stat-display.c
++++ b/tools/perf/util/stat-display.c
+@@ -21,7 +21,6 @@
+ #include "iostat.h"
+ #include "pmu.h"
+ #include "pmus.h"
+-#include "tool_pmu.h"
+ #define CNTR_NOT_SUPPORTED    "<not supported>"
+ #define CNTR_NOT_COUNTED      "<not counted>"
+@@ -947,10 +946,7 @@ static bool should_skip_zero_counter(struct perf_stat_config *config,
+       if (config->aggr_mode == AGGR_THREAD && config->system_wide)
+               return true;
+-      /*
+-       * Many tool events are only gathered on the first index, skip other
+-       * zero values.
+-       */
++      /* Tool events have the software PMU but are only gathered on 1. */
+       if (evsel__is_tool(counter))
+               return true;
+diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c
+index caffdaa8be9a1..7c49997fab3a3 100644
+--- a/tools/perf/util/stat-shadow.c
++++ b/tools/perf/util/stat-shadow.c
+@@ -15,7 +15,6 @@
+ #include <linux/zalloc.h>
+ #include "iostat.h"
+ #include "util/hashmap.h"
+-#include "tool_pmu.h"
+ struct stats walltime_nsecs_stats;
+ struct rusage_stats ru_stats;
+diff --git a/tools/perf/util/tool_pmu.c b/tools/perf/util/tool_pmu.c
+deleted file mode 100644
+index f41fed39d70d8..0000000000000
+--- a/tools/perf/util/tool_pmu.c
++++ /dev/null
+@@ -1,411 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-only
+-#include "cgroup.h"
+-#include "counts.h"
+-#include "evsel.h"
+-#include "pmu.h"
+-#include "print-events.h"
+-#include "time-utils.h"
+-#include "tool_pmu.h"
+-#include <api/io.h>
+-#include <internal/threadmap.h>
+-#include <perf/threadmap.h>
+-#include <fcntl.h>
+-#include <strings.h>
+-
+-static const char *const tool_pmu__event_names[PERF_TOOL_MAX] = {
+-      NULL,
+-      "duration_time",
+-      "user_time",
+-      "system_time",
+-};
+-
+-
+-const char *perf_tool_event__to_str(enum perf_tool_event ev)
+-{
+-      if (ev > PERF_TOOL_NONE && ev < PERF_TOOL_MAX)
+-              return tool_pmu__event_names[ev];
+-
+-      return NULL;
+-}
+-
+-enum perf_tool_event perf_tool_event__from_str(const char *str)
+-{
+-      int i;
+-
+-      perf_tool_event__for_each_event(i) {
+-              if (!strcasecmp(str, tool_pmu__event_names[i]))
+-                      return i;
+-      }
+-      return PERF_TOOL_NONE;
+-}
+-
+-static int tool_pmu__config_term(struct perf_event_attr *attr,
+-                               struct parse_events_term *term,
+-                               struct parse_events_error *err)
+-{
+-      if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) {
+-              enum perf_tool_event ev = perf_tool_event__from_str(term->config);
+-
+-              if (ev == PERF_TOOL_NONE)
+-                      goto err_out;
+-
+-              attr->config = ev;
+-              return 0;
+-      }
+-err_out:
+-      if (err) {
+-              char *err_str;
+-
+-              parse_events_error__handle(err, term->err_val,
+-                                      asprintf(&err_str,
+-                                              "unexpected tool event term (%s) %s",
+-                                              parse_events__term_type_str(term->type_term),
+-                                              term->config) < 0
+-                                      ? strdup("unexpected tool event term")
+-                                      : err_str,
+-                                      NULL);
+-      }
+-      return -EINVAL;
+-}
+-
+-int tool_pmu__config_terms(struct perf_event_attr *attr,
+-                         struct parse_events_terms *terms,
+-                         struct parse_events_error *err)
+-{
+-      struct parse_events_term *term;
+-
+-      list_for_each_entry(term, &terms->terms, list) {
+-              if (tool_pmu__config_term(attr, term, err))
+-                      return -EINVAL;
+-      }
+-
+-      return 0;
+-
+-}
+-
+-int tool_pmu__for_each_event_cb(struct perf_pmu *pmu, void *state, pmu_event_callback cb)
+-{
+-      struct pmu_event_info info = {
+-              .pmu = pmu,
+-              .event_type_desc = "Tool event",
+-      };
+-      int i;
+-
+-      perf_tool_event__for_each_event(i) {
+-              int ret;
+-
+-              info.name = perf_tool_event__to_str(i);
+-              info.alias = NULL;
+-              info.scale_unit = NULL;
+-              info.desc = NULL;
+-              info.long_desc = NULL;
+-              info.encoding_desc = NULL;
+-              info.topic = NULL;
+-              info.pmu_name = pmu->name;
+-              info.deprecated = false;
+-              ret = cb(state, &info);
+-              if (ret)
+-                      return ret;
+-      }
+-      return 0;
+-}
+-
+-bool perf_pmu__is_tool(const struct perf_pmu *pmu)
+-{
+-      return pmu && pmu->type == PERF_PMU_TYPE_TOOL;
+-}
+-
+-bool evsel__is_tool(const struct evsel *evsel)
+-{
+-      return perf_pmu__is_tool(evsel->pmu);
+-}
+-
+-enum perf_tool_event evsel__tool_event(const struct evsel *evsel)
+-{
+-      if (!evsel__is_tool(evsel))
+-              return PERF_TOOL_NONE;
+-
+-      return (enum perf_tool_event)evsel->core.attr.config;
+-}
+-
+-const char *evsel__tool_pmu_event_name(const struct evsel *evsel)
+-{
+-      return perf_tool_event__to_str(evsel->core.attr.config);
+-}
+-
+-static bool read_until_char(struct io *io, char e)
+-{
+-      int c;
+-
+-      do {
+-              c = io__get_char(io);
+-              if (c == -1)
+-                      return false;
+-      } while (c != e);
+-      return true;
+-}
+-
+-static int read_stat_field(int fd, struct perf_cpu cpu, int field, __u64 *val)
+-{
+-      char buf[256];
+-      struct io io;
+-      int i;
+-
+-      io__init(&io, fd, buf, sizeof(buf));
+-
+-      /* Skip lines to relevant CPU. */
+-      for (i = -1; i < cpu.cpu; i++) {
+-              if (!read_until_char(&io, '\n'))
+-                      return -EINVAL;
+-      }
+-      /* Skip to "cpu". */
+-      if (io__get_char(&io) != 'c') return -EINVAL;
+-      if (io__get_char(&io) != 'p') return -EINVAL;
+-      if (io__get_char(&io) != 'u') return -EINVAL;
+-
+-      /* Skip N of cpuN. */
+-      if (!read_until_char(&io, ' '))
+-              return -EINVAL;
+-
+-      i = 1;
+-      while (true) {
+-              if (io__get_dec(&io, val) != ' ')
+-                      break;
+-              if (field == i)
+-                      return 0;
+-              i++;
+-      }
+-      return -EINVAL;
+-}
+-
+-static int read_pid_stat_field(int fd, int field, __u64 *val)
+-{
+-      char buf[256];
+-      struct io io;
+-      int c, i;
+-
+-      io__init(&io, fd, buf, sizeof(buf));
+-      if (io__get_dec(&io, val) != ' ')
+-              return -EINVAL;
+-      if (field == 1)
+-              return 0;
+-
+-      /* Skip comm. */
+-      if (io__get_char(&io) != '(' || !read_until_char(&io, ')'))
+-              return -EINVAL;
+-      if (field == 2)
+-              return -EINVAL; /* String can't be returned. */
+-
+-      /* Skip state */
+-      if (io__get_char(&io) != ' ' || io__get_char(&io) == -1)
+-              return -EINVAL;
+-      if (field == 3)
+-              return -EINVAL; /* String can't be returned. */
+-
+-      /* Loop over numeric fields*/
+-      if (io__get_char(&io) != ' ')
+-              return -EINVAL;
+-
+-      i = 4;
+-      while (true) {
+-              c = io__get_dec(&io, val);
+-              if (c == -1)
+-                      return -EINVAL;
+-              if (c == -2) {
+-                      /* Assume a -ve was read */
+-                      c = io__get_dec(&io, val);
+-                      *val *= -1;
+-              }
+-              if (c != ' ')
+-                      return -EINVAL;
+-              if (field == i)
+-                      return 0;
+-              i++;
+-      }
+-      return -EINVAL;
+-}
+-
+-int evsel__tool_pmu_prepare_open(struct evsel *evsel,
+-                               struct perf_cpu_map *cpus,
+-                               int nthreads)
+-{
+-      if ((evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME ||
+-           evsel__tool_event(evsel) == PERF_TOOL_USER_TIME) &&
+-          !evsel->start_times) {
+-              evsel->start_times = xyarray__new(perf_cpu_map__nr(cpus),
+-                                                nthreads,
+-                                                sizeof(__u64));
+-              if (!evsel->start_times)
+-                      return -ENOMEM;
+-      }
+-      return 0;
+-}
+-
+-#define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
+-
+-int evsel__tool_pmu_open(struct evsel *evsel,
+-                       struct perf_thread_map *threads,
+-                       int start_cpu_map_idx, int end_cpu_map_idx)
+-{
+-      enum perf_tool_event ev = evsel__tool_event(evsel);
+-      int pid = -1, idx = 0, thread = 0, nthreads, err = 0, old_errno;
+-
+-      if (ev == PERF_TOOL_DURATION_TIME) {
+-              if (evsel->core.attr.sample_period) /* no sampling */
+-                      return -EINVAL;
+-              evsel->start_time = rdclock();
+-              return 0;
+-      }
+-
+-      if (evsel->cgrp)
+-              pid = evsel->cgrp->fd;
+-
+-      nthreads = perf_thread_map__nr(threads);
+-      for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) {
+-              for (thread = 0; thread < nthreads; thread++) {
+-                      if (thread >= nthreads)
+-                              break;
+-
+-                      if (!evsel->cgrp && !evsel->core.system_wide)
+-                              pid = perf_thread_map__pid(threads, thread);
+-
+-                      if (ev == PERF_TOOL_USER_TIME || ev == PERF_TOOL_SYSTEM_TIME) {
+-                              bool system = ev == PERF_TOOL_SYSTEM_TIME;
+-                              __u64 *start_time = NULL;
+-                              int fd;
+-
+-                              if (evsel->core.attr.sample_period) {
+-                                      /* no sampling */
+-                                      err = -EINVAL;
+-                                      goto out_close;
+-                              }
+-                              if (pid > -1) {
+-                                      char buf[64];
+-
+-                                      snprintf(buf, sizeof(buf), "/proc/%d/stat", pid);
+-                                      fd = open(buf, O_RDONLY);
+-                                      evsel->pid_stat = true;
+-                              } else {
+-                                      fd = open("/proc/stat", O_RDONLY);
+-                              }
+-                              FD(evsel, idx, thread) = fd;
+-                              if (fd < 0) {
+-                                      err = -errno;
+-                                      goto out_close;
+-                              }
+-                              start_time = xyarray__entry(evsel->start_times, idx, thread);
+-                              if (pid > -1) {
+-                                      err = read_pid_stat_field(fd, system ? 15 : 14,
+-                                                                start_time);
+-                              } else {
+-                                      struct perf_cpu cpu;
+-
+-                                      cpu = perf_cpu_map__cpu(evsel->core.cpus, idx);
+-                                      err = read_stat_field(fd, cpu, system ? 3 : 1,
+-                                                            start_time);
+-                              }
+-                              if (err)
+-                                      goto out_close;
+-                      }
+-
+-              }
+-      }
+-      return 0;
+-out_close:
+-      if (err)
+-              threads->err_thread = thread;
+-
+-      old_errno = errno;
+-      do {
+-              while (--thread >= 0) {
+-                      if (FD(evsel, idx, thread) >= 0)
+-                              close(FD(evsel, idx, thread));
+-                      FD(evsel, idx, thread) = -1;
+-              }
+-              thread = nthreads;
+-      } while (--idx >= 0);
+-      errno = old_errno;
+-      return err;
+-}
+-
+-int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread)
+-{
+-      __u64 *start_time, cur_time, delta_start;
+-      int fd, err = 0;
+-      struct perf_counts_values *count;
+-      bool adjust = false;
+-
+-      count = perf_counts(evsel->counts, cpu_map_idx, thread);
+-
+-      switch (evsel__tool_event(evsel)) {
+-      case PERF_TOOL_DURATION_TIME:
+-              /*
+-               * Pretend duration_time is only on the first CPU and thread, or
+-               * else aggregation will scale duration_time by the number of
+-               * CPUs/threads.
+-               */
+-              start_time = &evsel->start_time;
+-              if (cpu_map_idx == 0 && thread == 0)
+-                      cur_time = rdclock();
+-              else
+-                      cur_time = *start_time;
+-              break;
+-      case PERF_TOOL_USER_TIME:
+-      case PERF_TOOL_SYSTEM_TIME: {
+-              bool system = evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME;
+-
+-              start_time = xyarray__entry(evsel->start_times, cpu_map_idx, thread);
+-              fd = FD(evsel, cpu_map_idx, thread);
+-              lseek(fd, SEEK_SET, 0);
+-              if (evsel->pid_stat) {
+-                      /* The event exists solely on 1 CPU. */
+-                      if (cpu_map_idx == 0)
+-                              err = read_pid_stat_field(fd, system ? 15 : 14, &cur_time);
+-                      else
+-                              cur_time = 0;
+-              } else {
+-                      /* The event is for all threads. */
+-                      if (thread == 0) {
+-                              struct perf_cpu cpu = perf_cpu_map__cpu(evsel->core.cpus,
+-                                                                      cpu_map_idx);
+-
+-                              err = read_stat_field(fd, cpu, system ? 3 : 1, &cur_time);
+-                      } else {
+-                              cur_time = 0;
+-                      }
+-              }
+-              adjust = true;
+-              break;
+-      }
+-      case PERF_TOOL_NONE:
+-      case PERF_TOOL_MAX:
+-      default:
+-              err = -EINVAL;
+-      }
+-      if (err)
+-              return err;
+-
+-      delta_start = cur_time - *start_time;
+-      if (adjust) {
+-              __u64 ticks_per_sec = sysconf(_SC_CLK_TCK);
+-
+-              delta_start *= 1000000000 / ticks_per_sec;
+-      }
+-      count->val    = delta_start;
+-      count->ena    = count->run = delta_start;
+-      count->lost   = 0;
+-      return 0;
+-}
+-
+-struct perf_pmu *perf_pmus__tool_pmu(void)
+-{
+-      static struct perf_pmu tool = {
+-              .name = "tool",
+-              .type = PERF_PMU_TYPE_TOOL,
+-              .aliases = LIST_HEAD_INIT(tool.aliases),
+-              .caps = LIST_HEAD_INIT(tool.caps),
+-              .format = LIST_HEAD_INIT(tool.format),
+-      };
+-
+-      return &tool;
+-}
+diff --git a/tools/perf/util/tool_pmu.h b/tools/perf/util/tool_pmu.h
+deleted file mode 100644
+index 05a4052c8b9d8..0000000000000
+--- a/tools/perf/util/tool_pmu.h
++++ /dev/null
+@@ -1,51 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
+-#ifndef __TOOL_PMU_H
+-#define __TOOL_PMU_H
+-
+-#include "pmu.h"
+-
+-struct evsel;
+-struct perf_thread_map;
+-struct print_callbacks;
+-
+-enum perf_tool_event {
+-      PERF_TOOL_NONE = 0,
+-      PERF_TOOL_DURATION_TIME = 1,
+-      PERF_TOOL_USER_TIME = 2,
+-      PERF_TOOL_SYSTEM_TIME = 3,
+-
+-      PERF_TOOL_MAX,
+-};
+-
+-#define perf_tool_event__for_each_event(ev)                           \
+-      for ((ev) = PERF_TOOL_DURATION_TIME; (ev) < PERF_TOOL_MAX; ev++)
+-
+-static inline size_t tool_pmu__num_events(void)
+-{
+-      return PERF_TOOL_MAX - 1;
+-}
+-
+-const char *perf_tool_event__to_str(enum perf_tool_event ev);
+-enum perf_tool_event perf_tool_event__from_str(const char *str);
+-int tool_pmu__config_terms(struct perf_event_attr *attr,
+-                         struct parse_events_terms *terms,
+-                         struct parse_events_error *err);
+-int tool_pmu__for_each_event_cb(struct perf_pmu *pmu, void *state, pmu_event_callback cb);
+-
+-bool perf_pmu__is_tool(const struct perf_pmu *pmu);
+-
+-
+-bool evsel__is_tool(const struct evsel *evsel);
+-enum perf_tool_event evsel__tool_event(const struct evsel *evsel);
+-const char *evsel__tool_pmu_event_name(const struct evsel *evsel);
+-int evsel__tool_pmu_prepare_open(struct evsel *evsel,
+-                               struct perf_cpu_map *cpus,
+-                               int nthreads);
+-int evsel__tool_pmu_open(struct evsel *evsel,
+-                       struct perf_thread_map *threads,
+-                       int start_cpu_map_idx, int end_cpu_map_idx);
+-int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread);
+-
+-struct perf_pmu *perf_pmus__tool_pmu(void);
+-
+-#endif /* __TOOL_PMU_H */
+-- 
+2.53.0
+
diff --git a/queue-6.12/revert-perf-tool_pmu-fix-aggregation-on-duration_tim.patch b/queue-6.12/revert-perf-tool_pmu-fix-aggregation-on-duration_tim.patch
new file mode 100644 (file)
index 0000000..2db63cf
--- /dev/null
@@ -0,0 +1,35 @@
+From f126428e706b7c6eb9aa1c79ced284b195d4e392 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 24 May 2026 10:36:48 -0400
+Subject: Revert "perf tool_pmu: Fix aggregation on duration_time"
+
+This reverts commit 310be445ab1028315627b326516f193511cb1c97.
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ tools/perf/util/tool_pmu.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/tools/perf/util/tool_pmu.c b/tools/perf/util/tool_pmu.c
+index 3d1d6b3352ec7..f41fed39d70d8 100644
+--- a/tools/perf/util/tool_pmu.c
++++ b/tools/perf/util/tool_pmu.c
+@@ -392,14 +392,8 @@ int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread)
+               delta_start *= 1000000000 / ticks_per_sec;
+       }
+       count->val    = delta_start;
++      count->ena    = count->run = delta_start;
+       count->lost   = 0;
+-      /*
+-       * The values of enabled and running must make a ratio of 100%. The
+-       * exact values don't matter as long as they are non-zero to avoid
+-       * issues with evsel__count_has_error.
+-       */
+-      count->ena++;
+-      count->run++;
+       return 0;
+ }
+-- 
+2.53.0
+
index a404786c1046c6ecc3b8d1efafdca3731ae6bd55..f136b34c9949249e52a8255edde44113dfea94a8 100644 (file)
@@ -5,3 +5,7 @@ mptcp-pm-add_addr-rtx-free-sk-if-last.patch
 ksmbd-validate-owner-of-durable-handle-on-reconnect.patch
 drm-xe-hdcp-add-null-check-for-media_gt-in-intel_hdc.patch
 s390-debug-reject-zero-length-input-before-trimming-.patch
+revert-perf-cgroup-update-metric-leader-in-evlist__e.patch
+revert-perf-tool_pmu-fix-aggregation-on-duration_tim.patch
+revert-perf-python-add-parse_events-function.patch
+revert-perf-tool_pmu-factor-tool-events-into-their-o.patch
diff --git a/queue-6.18/fuse-fix-uninit-value-in-fuse_dentry_revalidate.patch b/queue-6.18/fuse-fix-uninit-value-in-fuse_dentry_revalidate.patch
new file mode 100644 (file)
index 0000000..e257ac8
--- /dev/null
@@ -0,0 +1,101 @@
+From bb0d96d2445909573fe0f18ad16f6c00eb791228 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 16 Feb 2026 14:48:30 +0000
+Subject: fuse: fix uninit-value in fuse_dentry_revalidate()
+
+From: Luis Henriques <luis@igalia.com>
+
+commit 5a6baf204610589f8a5b5a1cd69d1fe661d9d3cd upstream.
+
+fuse_dentry_revalidate() may be called with a dentry that didn't had
+->d_time initialised.  The issue was found with KMSAN, where lookup_open()
+calls __d_alloc(), followed by d_revalidate(), as shown below:
+
+=====================================================
+BUG: KMSAN: uninit-value in fuse_dentry_revalidate+0x150/0x13d0 fs/fuse/dir.c:394
+ fuse_dentry_revalidate+0x150/0x13d0 fs/fuse/dir.c:394
+ d_revalidate fs/namei.c:1030 [inline]
+ lookup_open fs/namei.c:4405 [inline]
+ open_last_lookups fs/namei.c:4583 [inline]
+ path_openat+0x1614/0x64c0 fs/namei.c:4827
+ do_file_open+0x2aa/0x680 fs/namei.c:4859
+[...]
+
+Uninit was created at:
+ slab_post_alloc_hook mm/slub.c:4466 [inline]
+ slab_alloc_node mm/slub.c:4788 [inline]
+ kmem_cache_alloc_lru_noprof+0x382/0x1280 mm/slub.c:4807
+ __d_alloc+0x55/0xa00 fs/dcache.c:1740
+ d_alloc_parallel+0x99/0x2740 fs/dcache.c:2604
+ lookup_open fs/namei.c:4398 [inline]
+ open_last_lookups fs/namei.c:4583 [inline]
+ path_openat+0x135f/0x64c0 fs/namei.c:4827
+ do_file_open+0x2aa/0x680 fs/namei.c:4859
+[...]
+=====================================================
+
+Reported-by: syzbot+fdebb2dc960aa56c600a@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/all/69917e0d.050a0220.340abe.02e2.GAE@google.com
+Fixes: 2396356a945b ("fuse: add more control over cache invalidation behaviour")
+Signed-off-by: Luis Henriques <luis@igalia.com>
+Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/fuse/dir.c | 20 +++++++++++++++-----
+ 1 file changed, 15 insertions(+), 5 deletions(-)
+
+diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
+index ecaec0fea3a13..1bc6982b5d6aa 100644
+--- a/fs/fuse/dir.c
++++ b/fs/fuse/dir.c
+@@ -283,21 +283,33 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name,
+       goto out;
+ }
+-#if BITS_PER_LONG < 64
+ static int fuse_dentry_init(struct dentry *dentry)
+ {
++      int ret = 0;
++
++      /*
++       * Initialising d_time (epoch) to '0' ensures the dentry is invalid
++       * if compared to fc->epoch, which is initialized to '1'.
++       */
++      dentry->d_time = 0;
++
++#if BITS_PER_LONG < 64
+       dentry->d_fsdata = kzalloc(sizeof(union fuse_dentry),
+                                  GFP_KERNEL_ACCOUNT | __GFP_RECLAIMABLE);
+-      return dentry->d_fsdata ? 0 : -ENOMEM;
++      ret = dentry->d_fsdata ? 0 : -ENOMEM;
++#endif
++
++      return ret;
+ }
+ static void fuse_dentry_release(struct dentry *dentry)
+ {
++#if BITS_PER_LONG < 64
+       union fuse_dentry *fd = dentry->d_fsdata;
+       kfree_rcu(fd, rcu);
+-}
+ #endif
++}
+ static int fuse_dentry_delete(const struct dentry *dentry)
+ {
+@@ -331,10 +343,8 @@ static struct vfsmount *fuse_dentry_automount(struct path *path)
+ const struct dentry_operations fuse_dentry_operations = {
+       .d_revalidate   = fuse_dentry_revalidate,
+       .d_delete       = fuse_dentry_delete,
+-#if BITS_PER_LONG < 64
+       .d_init         = fuse_dentry_init,
+       .d_release      = fuse_dentry_release,
+-#endif
+       .d_automount    = fuse_dentry_automount,
+ };
+-- 
+2.53.0
+
index 9ec974050ced0aef8663273f01e5e6c1f108b199..083cb74b536f2dbf33d8ee540c851da18871d600 100644 (file)
@@ -1,3 +1,4 @@
 drm-xe-hdcp-add-null-check-for-media_gt-in-intel_hdc.patch
 iommu-amd-fix-illegal-cap-mmio-access-in-iommu-debug.patch
 iommu-amd-remove-latent-out-of-bounds-access-in-iomm.patch
+fuse-fix-uninit-value-in-fuse_dentry_revalidate.patch