From 725c9955fc96c3536847a836b2ff9570efec6770 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Sun, 24 May 2026 11:21:55 -0400 Subject: [PATCH] Fixes for all trees Signed-off-by: Sasha Levin --- ...dso-fix-output-operand-size-of-rdpid.patch | 42 + queue-5.10/series | 1 + ...dso-fix-output-operand-size-of-rdpid.patch | 42 + queue-5.15/series | 1 + ...up-update-metric-leader-in-evlist__e.patch | 79 + ...erf-python-add-parse_events-function.patch | 174 ++ ..._pmu-factor-tool-events-into-their-o.patch | 1430 +++++++++++++++++ ..._pmu-fix-aggregation-on-duration_tim.patch | 35 + queue-6.12/series | 4 + ...init-value-in-fuse_dentry_revalidate.patch | 101 ++ queue-6.18/series | 1 + 11 files changed, 1910 insertions(+) create mode 100644 queue-5.10/revert-x86-vdso-fix-output-operand-size-of-rdpid.patch create mode 100644 queue-5.15/revert-x86-vdso-fix-output-operand-size-of-rdpid.patch create mode 100644 queue-6.12/revert-perf-cgroup-update-metric-leader-in-evlist__e.patch create mode 100644 queue-6.12/revert-perf-python-add-parse_events-function.patch create mode 100644 queue-6.12/revert-perf-tool_pmu-factor-tool-events-into-their-o.patch create mode 100644 queue-6.12/revert-perf-tool_pmu-fix-aggregation-on-duration_tim.patch create mode 100644 queue-6.18/fuse-fix-uninit-value-in-fuse_dentry_revalidate.patch 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 index 0000000000..b5cef3c80e --- /dev/null +++ b/queue-5.10/revert-x86-vdso-fix-output-operand-size-of-rdpid.patch @@ -0,0 +1,42 @@ +From 32600af84e48563ff5139c5c3d6b594369d3c5c8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 +--- + 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 + diff --git a/queue-5.10/series b/queue-5.10/series index 18d017436b..07a3e8f738 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -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 index 0000000000..cf8dff1955 --- /dev/null +++ b/queue-5.15/revert-x86-vdso-fix-output-operand-size-of-rdpid.patch @@ -0,0 +1,42 @@ +From 3693047bc8cebd45a594b6bf345affc275538e9a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 +--- + 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 + diff --git a/queue-5.15/series b/queue-5.15/series index fe706386e2..4dc6405915 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -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 index 0000000000..e1dca8ad29 --- /dev/null +++ b/queue-6.12/revert-perf-cgroup-update-metric-leader-in-evlist__e.patch @@ -0,0 +1,79 @@ +From 5c9ceee75a90cd5db0d38d9dbfde6659194a9b5b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 +--- + 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 index 0000000000..59f2242923 --- /dev/null +++ b/queue-6.12/revert-perf-python-add-parse_events-function.patch @@ -0,0 +1,174 @@ +From 8a11c59a5aab9423777ee1ead5e66174896c7adb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 +--- + 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 index 0000000000..6d2806f2ea --- /dev/null +++ b/queue-6.12/revert-perf-tool_pmu-factor-tool-events-into-their-o.patch @@ -0,0 +1,1430 @@ +From 0a56a56da306fbc202f01e7373295d6239716451 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 +--- + 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 + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 + #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 +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 PE_VALUE + %type PE_VALUE_SYM_HW + %type PE_VALUE_SYM_SW ++%type PE_VALUE_SYM_TOOL + %type PE_MODIFIER_EVENT + %type PE_TERM + %type 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 + #include + #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 /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 "" + #define CNTR_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 + #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 +-#include +-#include +-#include +-#include +- +-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 index 0000000000..2db63cf639 --- /dev/null +++ b/queue-6.12/revert-perf-tool_pmu-fix-aggregation-on-duration_tim.patch @@ -0,0 +1,35 @@ +From f126428e706b7c6eb9aa1c79ced284b195d4e392 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 +--- + 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 + diff --git a/queue-6.12/series b/queue-6.12/series index a404786c10..f136b34c99 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -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 index 0000000000..e257ac8963 --- /dev/null +++ b/queue-6.18/fuse-fix-uninit-value-in-fuse_dentry_revalidate.patch @@ -0,0 +1,101 @@ +From bb0d96d2445909573fe0f18ad16f6c00eb791228 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 16 Feb 2026 14:48:30 +0000 +Subject: fuse: fix uninit-value in fuse_dentry_revalidate() + +From: Luis Henriques + +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 +Signed-off-by: Miklos Szeredi +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.18/series b/queue-6.18/series index 9ec974050c..083cb74b53 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -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 -- 2.47.3