From: Anup Patel <apatel@ventanamicro.com>
To: Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
Paul Walmsley <paul.walmsley@sifive.com>,
Palmer Dabbelt <palmer@dabbelt.com>,
Greg KH <gregkh@linuxfoundation.org>,
Alexander Shishkin <alexander.shishkin@linux.intel.com>,
Ian Rogers <irogers@google.com>
Cc: Mark Rutland <mark.rutland@arm.com>,
devicetree@vger.kernel.org, Alexandre Ghiti <alex@ghiti.fr>,
Atish Patra <atish.patra@linux.dev>,
Peter Zijlstra <peterz@infradead.org>,
Anup Patel <anup@brainfault.org>,
Adrian Hunter <adrian.hunter@intel.com>,
linux-kernel@vger.kernel.org,
Mayuresh Chitale <mchitale@ventanamicro.com>,
Ingo Molnar <mingo@redhat.com>, Jiri Olsa <jolsa@kernel.org>,
Anup Patel <apatel@ventanamicro.com>,
Mayuresh Chitale <mchitale@gmail.com>,
Namhyung Kim <namhyung@kernel.org>,
linux-riscv@lists.infradead.org,
Andrew Jones <ajones@ventanamicro.com>,
Liang Kan <kan.liang@linux.intel.com>
Subject: [PATCH 08/11] rvtrace: Add perf driver for tracing using perf tool
Date: Thu, 2 Oct 2025 11:37:29 +0530 [thread overview]
Message-ID: <20251002060732.100213-9-apatel@ventanamicro.com> (raw)
In-Reply-To: <20251002060732.100213-1-apatel@ventanamicro.com>
From: Mayuresh Chitale <mchitale@ventanamicro.com>
Add perf driver for RISC-V tracing similar to ARM Coresight and Hisilicon
PTT drivers. The driver adds 'rvtrace' event descriptor which can be used
by the perf tool to record the RISC-V trace data.
Co-developed-by: Anup Patel <apatel@ventanamicro.com>
Signed-off-by: Anup Patel <apatel@ventanamicro.com>
Signed-off-by: Mayuresh Chitale <mchitale@ventanamicro.com>
---
drivers/hwtracing/rvtrace/Kconfig | 1 +
drivers/hwtracing/rvtrace/Makefile | 2 +-
drivers/hwtracing/rvtrace/rvtrace-core.c | 8 +
drivers/hwtracing/rvtrace/rvtrace-perf.c | 343 +++++++++++++++++++++++
include/linux/rvtrace.h | 3 +
5 files changed, 356 insertions(+), 1 deletion(-)
create mode 100644 drivers/hwtracing/rvtrace/rvtrace-perf.c
diff --git a/drivers/hwtracing/rvtrace/Kconfig b/drivers/hwtracing/rvtrace/Kconfig
index aef7e9989165..76379c63c5c3 100644
--- a/drivers/hwtracing/rvtrace/Kconfig
+++ b/drivers/hwtracing/rvtrace/Kconfig
@@ -4,6 +4,7 @@ menuconfig RVTRACE
tristate "RISC-V Trace Support"
depends on RISCV
depends on OF
+ select PERF_EVENTS
default RISCV
help
This framework provides a kernel interface for the RISC-V trace
diff --git a/drivers/hwtracing/rvtrace/Makefile b/drivers/hwtracing/rvtrace/Makefile
index 122e575da9fb..07403f4d94e3 100644
--- a/drivers/hwtracing/rvtrace/Makefile
+++ b/drivers/hwtracing/rvtrace/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_RVTRACE) += rvtrace.o
-rvtrace-y := rvtrace-core.o rvtrace-platform.o
+rvtrace-y := rvtrace-core.o rvtrace-platform.o rvtrace-perf.o
obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
obj-$(CONFIG_RVTRACE_RAMSINK) += rvtrace-ramsink.o
diff --git a/drivers/hwtracing/rvtrace/rvtrace-core.c b/drivers/hwtracing/rvtrace/rvtrace-core.c
index e874899c8b43..5d4e92f22ccd 100644
--- a/drivers/hwtracing/rvtrace/rvtrace-core.c
+++ b/drivers/hwtracing/rvtrace/rvtrace-core.c
@@ -760,11 +760,19 @@ static int __init rvtrace_init(void)
return ret;
}
+ ret = rvtrace_perf_init();
+ if (ret) {
+ platform_driver_unregister(&rvtrace_platform_driver);
+ bus_unregister(&rvtrace_bustype);
+ return ret;
+ }
+
return 0;
}
static void __exit rvtrace_exit(void)
{
+ rvtrace_perf_exit();
platform_driver_unregister(&rvtrace_platform_driver);
bus_unregister(&rvtrace_bustype);
}
diff --git a/drivers/hwtracing/rvtrace/rvtrace-perf.c b/drivers/hwtracing/rvtrace/rvtrace-perf.c
new file mode 100644
index 000000000000..2d3039f8b681
--- /dev/null
+++ b/drivers/hwtracing/rvtrace/rvtrace-perf.c
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2025 Ventanamicro Limited. All rights reserved.
+ * Author: Mayuresh Chitale <mchitale@venanamicro.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cpumask.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/perf_event.h>
+#include <linux/vmalloc.h>
+#include <linux/percpu-defs.h>
+#include <linux/slab.h>
+#include <linux/stringhash.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <linux/rvtrace.h>
+
+#define RVTRACE_PMU_NAME "rvtrace"
+#define RVTRACE_BUF_LEN (4 * 1024 * 1024)
+
+static struct pmu rvtrace_pmu;
+static DEFINE_SPINLOCK(perf_buf_lock);
+
+/**
+ * struct rvtrace_event_data - RISC-V trace specific perf event data
+ * @work: Handle to free allocated memory outside IRQ context.
+ * @mask: Hold the CPU(s) this event was set for.
+ * @aux_hwid_done: Whether a CPU has emitted the TraceID packet or not.
+ * @path: An array of path, each slot for one CPU.
+ * @buf: Aux buffer / pages allocated by perf framework.
+ */
+struct rvtrace_event_data {
+ struct work_struct work;
+ cpumask_t mask;
+ cpumask_t aux_hwid_done;
+ struct rvtrace_path * __percpu *path;
+ struct rvtrace_perf_auxbuf buf;
+};
+
+struct rvtrace_ctxt {
+ struct perf_output_handle handle;
+ struct rvtrace_event_data *event_data;
+};
+
+static DEFINE_PER_CPU(struct rvtrace_ctxt, rvtrace_ctxt);
+
+static void *alloc_event_data(int cpu)
+{
+ struct rvtrace_event_data *event_data;
+ cpumask_t *mask;
+
+ event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
+ if (!event_data)
+ return NULL;
+
+ /* Update mask as per selected CPUs */
+ mask = &event_data->mask;
+ if (cpu != -1)
+ cpumask_set_cpu(cpu, mask);
+ else
+ cpumask_copy(mask, cpu_present_mask);
+
+ event_data->path = alloc_percpu(struct rvtrace_path *);
+ return event_data;
+}
+
+static void rvtrace_free_aux(void *data)
+{
+ struct rvtrace_event_data *event_data = data;
+
+ schedule_work(&event_data->work);
+}
+
+static struct rvtrace_path **rvtrace_event_cpu_path_ptr(struct rvtrace_event_data *data,
+ int cpu)
+{
+ return per_cpu_ptr(data->path, cpu);
+}
+
+static void free_event_data(struct work_struct *work)
+{
+ struct rvtrace_event_data *event_data;
+ struct rvtrace_path *path;
+ cpumask_t *mask;
+ int cpu;
+
+ event_data = container_of(work, struct rvtrace_event_data, work);
+ mask = &event_data->mask;
+ for_each_cpu(cpu, mask) {
+ path = *rvtrace_event_cpu_path_ptr(event_data, cpu);
+ rvtrace_destroy_path(path);
+ }
+ free_percpu(event_data->path);
+ kfree(event_data);
+}
+
+static void *rvtrace_setup_aux(struct perf_event *event, void **pages,
+ int nr_pages, bool overwrite)
+{
+ struct rvtrace_event_data *event_data = NULL;
+ struct page **pagelist;
+ int cpu = event->cpu, i;
+ cpumask_t *mask;
+
+ event_data = alloc_event_data(cpu);
+ if (!event_data)
+ return NULL;
+
+ INIT_WORK(&event_data->work, free_event_data);
+ mask = &event_data->mask;
+ /*
+ * Create the path for each CPU in the mask. In case of any failure skip the CPU
+ */
+ for_each_cpu(cpu, mask) {
+ struct rvtrace_component *src;
+ struct rvtrace_path *path;
+
+ src = rvtrace_cpu_source(cpu);
+ if (!src)
+ continue;
+
+ path = rvtrace_create_path(src, NULL, RVTRACE_COMPONENT_MODE_PERF);
+ if (!path)
+ continue;
+
+ *rvtrace_event_cpu_path_ptr(event_data, cpu) = path;
+ }
+
+ /* If we don't have any CPUs ready for tracing, abort */
+ cpu = cpumask_first(&event_data->mask);
+ if (cpu >= nr_cpu_ids)
+ goto err;
+
+ pagelist = kcalloc(nr_pages, sizeof(*pagelist), GFP_KERNEL);
+ if (!pagelist)
+ goto err;
+
+ for (i = 0; i < nr_pages; i++)
+ pagelist[i] = virt_to_page(pages[i]);
+
+ event_data->buf.base = vmap(pagelist, nr_pages, VM_MAP, PAGE_KERNEL);
+ if (!event_data->buf.base) {
+ kfree(pagelist);
+ goto err;
+ }
+
+ event_data->buf.nr_pages = nr_pages;
+ event_data->buf.length = nr_pages * PAGE_SIZE;
+ event_data->buf.pos = 0;
+ return event_data;
+err:
+ rvtrace_free_aux(event_data);
+ return NULL;
+}
+
+static void rvtrace_event_read(struct perf_event *event)
+{
+}
+
+static void rvtrace_event_destroy(struct perf_event *event)
+{
+}
+
+static int rvtrace_event_init(struct perf_event *event)
+{
+ if (event->attr.type != rvtrace_pmu.type)
+ return -EINVAL;
+
+ event->destroy = rvtrace_event_destroy;
+ return 0;
+}
+
+static void rvtrace_event_start(struct perf_event *event, int flags)
+{
+ struct rvtrace_ctxt *ctxt = this_cpu_ptr(&rvtrace_ctxt);
+ struct perf_output_handle *handle = &ctxt->handle;
+ struct rvtrace_event_data *event_data;
+ int cpu = smp_processor_id();
+ struct rvtrace_path *path;
+
+ if (WARN_ON(ctxt->event_data))
+ goto fail;
+
+ /*
+ * Deal with the ring buffer API and get a handle on the
+ * session's information.
+ */
+ event_data = perf_aux_output_begin(handle, event);
+ if (!event_data)
+ goto fail;
+
+ if (!cpumask_test_cpu(cpu, &event_data->mask))
+ goto out;
+
+ event_data->buf.pos = handle->head % event_data->buf.length;
+ path = *rvtrace_event_cpu_path_ptr(event_data, cpu);
+ if (!path) {
+ pr_err("Error. Path not found\n");
+ return;
+ }
+
+ if (rvtrace_path_start(path)) {
+ pr_err("Error. Tracing not started\n");
+ return;
+ }
+
+ /*
+ * output cpu / trace ID in perf record, once for the lifetime
+ * of the event.
+ */
+ if (!cpumask_test_cpu(cpu, &event_data->aux_hwid_done)) {
+ cpumask_set_cpu(cpu, &event_data->aux_hwid_done);
+ perf_report_aux_output_id(event, cpu);
+ }
+
+out:
+ /* Tell the perf core the event is alive */
+ event->hw.state = 0;
+ ctxt->event_data = event_data;
+ return;
+fail:
+ event->hw.state = PERF_HES_STOPPED;
+}
+
+static void rvtrace_event_stop(struct perf_event *event, int mode)
+{
+ struct rvtrace_ctxt *ctxt = this_cpu_ptr(&rvtrace_ctxt);
+ struct perf_output_handle *handle = &ctxt->handle;
+ struct rvtrace_event_data *event_data;
+ int ret, cpu = smp_processor_id();
+ struct rvtrace_path *path;
+ size_t size;
+
+ if (event->hw.state == PERF_HES_STOPPED)
+ return;
+
+ if (handle->event &&
+ WARN_ON(perf_get_aux(handle) != ctxt->event_data))
+ return;
+
+ event_data = ctxt->event_data;
+ ctxt->event_data = NULL;
+
+ if (WARN_ON(!event_data))
+ return;
+
+ if (handle->event && (mode & PERF_EF_UPDATE) && !cpumask_test_cpu(cpu, &event_data->mask)) {
+ event->hw.state = PERF_HES_STOPPED;
+ perf_aux_output_end(handle, 0);
+ return;
+ }
+
+ /* stop tracing */
+ path = *rvtrace_event_cpu_path_ptr(event_data, cpu);
+ if (!path) {
+ pr_err("Error. Path not found\n");
+ return;
+ }
+
+ if (rvtrace_path_stop(path)) {
+ pr_err("Error. Tracing not stopped\n");
+ return;
+ }
+
+ event->hw.state = PERF_HES_STOPPED;
+ if (handle->event && (mode & PERF_EF_UPDATE)) {
+ if (WARN_ON_ONCE(handle->event != event))
+ return;
+ spin_lock(&perf_buf_lock);
+ ret = rvtrace_path_copyto_auxbuf(path, &event_data->buf, &size);
+ spin_unlock(&perf_buf_lock);
+ WARN_ON_ONCE(ret);
+ if (READ_ONCE(handle->event))
+ perf_aux_output_end(handle, size);
+ else
+ WARN_ON(size);
+ }
+}
+
+static int rvtrace_event_add(struct perf_event *event, int mode)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int ret = 0;
+
+ if (mode & PERF_EF_START) {
+ rvtrace_event_start(event, 0);
+ if (hwc->state & PERF_HES_STOPPED)
+ ret = -EINVAL;
+ } else {
+ hwc->state = PERF_HES_STOPPED;
+ }
+
+ return ret;
+}
+
+static void rvtrace_event_del(struct perf_event *event, int mode)
+{
+ rvtrace_event_stop(event, PERF_EF_UPDATE);
+}
+
+PMU_FORMAT_ATTR(event, "config:0-0");
+
+static struct attribute *rvtrace_pmu_formats_attr[] = {
+ &format_attr_event.attr,
+ NULL,
+};
+
+static struct attribute_group rvtrace_pmu_format_group = {
+ .name = "format",
+ .attrs = rvtrace_pmu_formats_attr,
+};
+
+static const struct attribute_group *rvtrace_pmu_attr_groups[] = {
+ &rvtrace_pmu_format_group,
+ NULL,
+};
+
+int __init rvtrace_perf_init(void)
+{
+ rvtrace_pmu.capabilities = (PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_ITRACE);
+ rvtrace_pmu.attr_groups = rvtrace_pmu_attr_groups;
+ rvtrace_pmu.task_ctx_nr = perf_sw_context;
+ rvtrace_pmu.read = rvtrace_event_read;
+ rvtrace_pmu.event_init = rvtrace_event_init;
+ rvtrace_pmu.setup_aux = rvtrace_setup_aux;
+ rvtrace_pmu.free_aux = rvtrace_free_aux;
+ rvtrace_pmu.start = rvtrace_event_start;
+ rvtrace_pmu.stop = rvtrace_event_stop;
+ rvtrace_pmu.add = rvtrace_event_add;
+ rvtrace_pmu.del = rvtrace_event_del;
+ rvtrace_pmu.module = THIS_MODULE;
+
+ return perf_pmu_register(&rvtrace_pmu, RVTRACE_PMU_NAME, -1);
+}
+
+void __exit rvtrace_perf_exit(void)
+{
+ perf_pmu_unregister(&rvtrace_pmu);
+}
diff --git a/include/linux/rvtrace.h b/include/linux/rvtrace.h
index 17d2fd9234c2..0a454707633b 100644
--- a/include/linux/rvtrace.h
+++ b/include/linux/rvtrace.h
@@ -335,4 +335,7 @@ static inline void rvtrace_unregister_driver(struct rvtrace_driver *rtdrv)
driver_unregister(&rtdrv->driver);
}
+int rvtrace_perf_init(void);
+void rvtrace_perf_exit(void);
+
#endif
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
WARNING: multiple messages have this Message-ID (diff)
From: Anup Patel <apatel@ventanamicro.com>
To: Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
Paul Walmsley <paul.walmsley@sifive.com>,
Palmer Dabbelt <palmer@dabbelt.com>,
Greg KH <gregkh@linuxfoundation.org>,
Alexander Shishkin <alexander.shishkin@linux.intel.com>,
Ian Rogers <irogers@google.com>
Cc: Alexandre Ghiti <alex@ghiti.fr>,
Peter Zijlstra <peterz@infradead.org>,
Ingo Molnar <mingo@redhat.com>,
Namhyung Kim <namhyung@kernel.org>,
Mark Rutland <mark.rutland@arm.com>, Jiri Olsa <jolsa@kernel.org>,
Adrian Hunter <adrian.hunter@intel.com>,
Liang Kan <kan.liang@linux.intel.com>,
Mayuresh Chitale <mchitale@gmail.com>,
Anup Patel <anup@brainfault.org>,
Atish Patra <atish.patra@linux.dev>,
Andrew Jones <ajones@ventanamicro.com>,
Sunil V L <sunilvl@ventanamicro.com>,
linux-riscv@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org,
Mayuresh Chitale <mchitale@ventanamicro.com>,
Anup Patel <apatel@ventanamicro.com>
Subject: [PATCH 08/11] rvtrace: Add perf driver for tracing using perf tool
Date: Thu, 2 Oct 2025 11:37:29 +0530 [thread overview]
Message-ID: <20251002060732.100213-9-apatel@ventanamicro.com> (raw)
In-Reply-To: <20251002060732.100213-1-apatel@ventanamicro.com>
From: Mayuresh Chitale <mchitale@ventanamicro.com>
Add perf driver for RISC-V tracing similar to ARM Coresight and Hisilicon
PTT drivers. The driver adds 'rvtrace' event descriptor which can be used
by the perf tool to record the RISC-V trace data.
Co-developed-by: Anup Patel <apatel@ventanamicro.com>
Signed-off-by: Anup Patel <apatel@ventanamicro.com>
Signed-off-by: Mayuresh Chitale <mchitale@ventanamicro.com>
---
drivers/hwtracing/rvtrace/Kconfig | 1 +
drivers/hwtracing/rvtrace/Makefile | 2 +-
drivers/hwtracing/rvtrace/rvtrace-core.c | 8 +
drivers/hwtracing/rvtrace/rvtrace-perf.c | 343 +++++++++++++++++++++++
include/linux/rvtrace.h | 3 +
5 files changed, 356 insertions(+), 1 deletion(-)
create mode 100644 drivers/hwtracing/rvtrace/rvtrace-perf.c
diff --git a/drivers/hwtracing/rvtrace/Kconfig b/drivers/hwtracing/rvtrace/Kconfig
index aef7e9989165..76379c63c5c3 100644
--- a/drivers/hwtracing/rvtrace/Kconfig
+++ b/drivers/hwtracing/rvtrace/Kconfig
@@ -4,6 +4,7 @@ menuconfig RVTRACE
tristate "RISC-V Trace Support"
depends on RISCV
depends on OF
+ select PERF_EVENTS
default RISCV
help
This framework provides a kernel interface for the RISC-V trace
diff --git a/drivers/hwtracing/rvtrace/Makefile b/drivers/hwtracing/rvtrace/Makefile
index 122e575da9fb..07403f4d94e3 100644
--- a/drivers/hwtracing/rvtrace/Makefile
+++ b/drivers/hwtracing/rvtrace/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_RVTRACE) += rvtrace.o
-rvtrace-y := rvtrace-core.o rvtrace-platform.o
+rvtrace-y := rvtrace-core.o rvtrace-platform.o rvtrace-perf.o
obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
obj-$(CONFIG_RVTRACE_RAMSINK) += rvtrace-ramsink.o
diff --git a/drivers/hwtracing/rvtrace/rvtrace-core.c b/drivers/hwtracing/rvtrace/rvtrace-core.c
index e874899c8b43..5d4e92f22ccd 100644
--- a/drivers/hwtracing/rvtrace/rvtrace-core.c
+++ b/drivers/hwtracing/rvtrace/rvtrace-core.c
@@ -760,11 +760,19 @@ static int __init rvtrace_init(void)
return ret;
}
+ ret = rvtrace_perf_init();
+ if (ret) {
+ platform_driver_unregister(&rvtrace_platform_driver);
+ bus_unregister(&rvtrace_bustype);
+ return ret;
+ }
+
return 0;
}
static void __exit rvtrace_exit(void)
{
+ rvtrace_perf_exit();
platform_driver_unregister(&rvtrace_platform_driver);
bus_unregister(&rvtrace_bustype);
}
diff --git a/drivers/hwtracing/rvtrace/rvtrace-perf.c b/drivers/hwtracing/rvtrace/rvtrace-perf.c
new file mode 100644
index 000000000000..2d3039f8b681
--- /dev/null
+++ b/drivers/hwtracing/rvtrace/rvtrace-perf.c
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2025 Ventanamicro Limited. All rights reserved.
+ * Author: Mayuresh Chitale <mchitale@venanamicro.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cpumask.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/perf_event.h>
+#include <linux/vmalloc.h>
+#include <linux/percpu-defs.h>
+#include <linux/slab.h>
+#include <linux/stringhash.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <linux/rvtrace.h>
+
+#define RVTRACE_PMU_NAME "rvtrace"
+#define RVTRACE_BUF_LEN (4 * 1024 * 1024)
+
+static struct pmu rvtrace_pmu;
+static DEFINE_SPINLOCK(perf_buf_lock);
+
+/**
+ * struct rvtrace_event_data - RISC-V trace specific perf event data
+ * @work: Handle to free allocated memory outside IRQ context.
+ * @mask: Hold the CPU(s) this event was set for.
+ * @aux_hwid_done: Whether a CPU has emitted the TraceID packet or not.
+ * @path: An array of path, each slot for one CPU.
+ * @buf: Aux buffer / pages allocated by perf framework.
+ */
+struct rvtrace_event_data {
+ struct work_struct work;
+ cpumask_t mask;
+ cpumask_t aux_hwid_done;
+ struct rvtrace_path * __percpu *path;
+ struct rvtrace_perf_auxbuf buf;
+};
+
+struct rvtrace_ctxt {
+ struct perf_output_handle handle;
+ struct rvtrace_event_data *event_data;
+};
+
+static DEFINE_PER_CPU(struct rvtrace_ctxt, rvtrace_ctxt);
+
+static void *alloc_event_data(int cpu)
+{
+ struct rvtrace_event_data *event_data;
+ cpumask_t *mask;
+
+ event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
+ if (!event_data)
+ return NULL;
+
+ /* Update mask as per selected CPUs */
+ mask = &event_data->mask;
+ if (cpu != -1)
+ cpumask_set_cpu(cpu, mask);
+ else
+ cpumask_copy(mask, cpu_present_mask);
+
+ event_data->path = alloc_percpu(struct rvtrace_path *);
+ return event_data;
+}
+
+static void rvtrace_free_aux(void *data)
+{
+ struct rvtrace_event_data *event_data = data;
+
+ schedule_work(&event_data->work);
+}
+
+static struct rvtrace_path **rvtrace_event_cpu_path_ptr(struct rvtrace_event_data *data,
+ int cpu)
+{
+ return per_cpu_ptr(data->path, cpu);
+}
+
+static void free_event_data(struct work_struct *work)
+{
+ struct rvtrace_event_data *event_data;
+ struct rvtrace_path *path;
+ cpumask_t *mask;
+ int cpu;
+
+ event_data = container_of(work, struct rvtrace_event_data, work);
+ mask = &event_data->mask;
+ for_each_cpu(cpu, mask) {
+ path = *rvtrace_event_cpu_path_ptr(event_data, cpu);
+ rvtrace_destroy_path(path);
+ }
+ free_percpu(event_data->path);
+ kfree(event_data);
+}
+
+static void *rvtrace_setup_aux(struct perf_event *event, void **pages,
+ int nr_pages, bool overwrite)
+{
+ struct rvtrace_event_data *event_data = NULL;
+ struct page **pagelist;
+ int cpu = event->cpu, i;
+ cpumask_t *mask;
+
+ event_data = alloc_event_data(cpu);
+ if (!event_data)
+ return NULL;
+
+ INIT_WORK(&event_data->work, free_event_data);
+ mask = &event_data->mask;
+ /*
+ * Create the path for each CPU in the mask. In case of any failure skip the CPU
+ */
+ for_each_cpu(cpu, mask) {
+ struct rvtrace_component *src;
+ struct rvtrace_path *path;
+
+ src = rvtrace_cpu_source(cpu);
+ if (!src)
+ continue;
+
+ path = rvtrace_create_path(src, NULL, RVTRACE_COMPONENT_MODE_PERF);
+ if (!path)
+ continue;
+
+ *rvtrace_event_cpu_path_ptr(event_data, cpu) = path;
+ }
+
+ /* If we don't have any CPUs ready for tracing, abort */
+ cpu = cpumask_first(&event_data->mask);
+ if (cpu >= nr_cpu_ids)
+ goto err;
+
+ pagelist = kcalloc(nr_pages, sizeof(*pagelist), GFP_KERNEL);
+ if (!pagelist)
+ goto err;
+
+ for (i = 0; i < nr_pages; i++)
+ pagelist[i] = virt_to_page(pages[i]);
+
+ event_data->buf.base = vmap(pagelist, nr_pages, VM_MAP, PAGE_KERNEL);
+ if (!event_data->buf.base) {
+ kfree(pagelist);
+ goto err;
+ }
+
+ event_data->buf.nr_pages = nr_pages;
+ event_data->buf.length = nr_pages * PAGE_SIZE;
+ event_data->buf.pos = 0;
+ return event_data;
+err:
+ rvtrace_free_aux(event_data);
+ return NULL;
+}
+
+static void rvtrace_event_read(struct perf_event *event)
+{
+}
+
+static void rvtrace_event_destroy(struct perf_event *event)
+{
+}
+
+static int rvtrace_event_init(struct perf_event *event)
+{
+ if (event->attr.type != rvtrace_pmu.type)
+ return -EINVAL;
+
+ event->destroy = rvtrace_event_destroy;
+ return 0;
+}
+
+static void rvtrace_event_start(struct perf_event *event, int flags)
+{
+ struct rvtrace_ctxt *ctxt = this_cpu_ptr(&rvtrace_ctxt);
+ struct perf_output_handle *handle = &ctxt->handle;
+ struct rvtrace_event_data *event_data;
+ int cpu = smp_processor_id();
+ struct rvtrace_path *path;
+
+ if (WARN_ON(ctxt->event_data))
+ goto fail;
+
+ /*
+ * Deal with the ring buffer API and get a handle on the
+ * session's information.
+ */
+ event_data = perf_aux_output_begin(handle, event);
+ if (!event_data)
+ goto fail;
+
+ if (!cpumask_test_cpu(cpu, &event_data->mask))
+ goto out;
+
+ event_data->buf.pos = handle->head % event_data->buf.length;
+ path = *rvtrace_event_cpu_path_ptr(event_data, cpu);
+ if (!path) {
+ pr_err("Error. Path not found\n");
+ return;
+ }
+
+ if (rvtrace_path_start(path)) {
+ pr_err("Error. Tracing not started\n");
+ return;
+ }
+
+ /*
+ * output cpu / trace ID in perf record, once for the lifetime
+ * of the event.
+ */
+ if (!cpumask_test_cpu(cpu, &event_data->aux_hwid_done)) {
+ cpumask_set_cpu(cpu, &event_data->aux_hwid_done);
+ perf_report_aux_output_id(event, cpu);
+ }
+
+out:
+ /* Tell the perf core the event is alive */
+ event->hw.state = 0;
+ ctxt->event_data = event_data;
+ return;
+fail:
+ event->hw.state = PERF_HES_STOPPED;
+}
+
+static void rvtrace_event_stop(struct perf_event *event, int mode)
+{
+ struct rvtrace_ctxt *ctxt = this_cpu_ptr(&rvtrace_ctxt);
+ struct perf_output_handle *handle = &ctxt->handle;
+ struct rvtrace_event_data *event_data;
+ int ret, cpu = smp_processor_id();
+ struct rvtrace_path *path;
+ size_t size;
+
+ if (event->hw.state == PERF_HES_STOPPED)
+ return;
+
+ if (handle->event &&
+ WARN_ON(perf_get_aux(handle) != ctxt->event_data))
+ return;
+
+ event_data = ctxt->event_data;
+ ctxt->event_data = NULL;
+
+ if (WARN_ON(!event_data))
+ return;
+
+ if (handle->event && (mode & PERF_EF_UPDATE) && !cpumask_test_cpu(cpu, &event_data->mask)) {
+ event->hw.state = PERF_HES_STOPPED;
+ perf_aux_output_end(handle, 0);
+ return;
+ }
+
+ /* stop tracing */
+ path = *rvtrace_event_cpu_path_ptr(event_data, cpu);
+ if (!path) {
+ pr_err("Error. Path not found\n");
+ return;
+ }
+
+ if (rvtrace_path_stop(path)) {
+ pr_err("Error. Tracing not stopped\n");
+ return;
+ }
+
+ event->hw.state = PERF_HES_STOPPED;
+ if (handle->event && (mode & PERF_EF_UPDATE)) {
+ if (WARN_ON_ONCE(handle->event != event))
+ return;
+ spin_lock(&perf_buf_lock);
+ ret = rvtrace_path_copyto_auxbuf(path, &event_data->buf, &size);
+ spin_unlock(&perf_buf_lock);
+ WARN_ON_ONCE(ret);
+ if (READ_ONCE(handle->event))
+ perf_aux_output_end(handle, size);
+ else
+ WARN_ON(size);
+ }
+}
+
+static int rvtrace_event_add(struct perf_event *event, int mode)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int ret = 0;
+
+ if (mode & PERF_EF_START) {
+ rvtrace_event_start(event, 0);
+ if (hwc->state & PERF_HES_STOPPED)
+ ret = -EINVAL;
+ } else {
+ hwc->state = PERF_HES_STOPPED;
+ }
+
+ return ret;
+}
+
+static void rvtrace_event_del(struct perf_event *event, int mode)
+{
+ rvtrace_event_stop(event, PERF_EF_UPDATE);
+}
+
+PMU_FORMAT_ATTR(event, "config:0-0");
+
+static struct attribute *rvtrace_pmu_formats_attr[] = {
+ &format_attr_event.attr,
+ NULL,
+};
+
+static struct attribute_group rvtrace_pmu_format_group = {
+ .name = "format",
+ .attrs = rvtrace_pmu_formats_attr,
+};
+
+static const struct attribute_group *rvtrace_pmu_attr_groups[] = {
+ &rvtrace_pmu_format_group,
+ NULL,
+};
+
+int __init rvtrace_perf_init(void)
+{
+ rvtrace_pmu.capabilities = (PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_ITRACE);
+ rvtrace_pmu.attr_groups = rvtrace_pmu_attr_groups;
+ rvtrace_pmu.task_ctx_nr = perf_sw_context;
+ rvtrace_pmu.read = rvtrace_event_read;
+ rvtrace_pmu.event_init = rvtrace_event_init;
+ rvtrace_pmu.setup_aux = rvtrace_setup_aux;
+ rvtrace_pmu.free_aux = rvtrace_free_aux;
+ rvtrace_pmu.start = rvtrace_event_start;
+ rvtrace_pmu.stop = rvtrace_event_stop;
+ rvtrace_pmu.add = rvtrace_event_add;
+ rvtrace_pmu.del = rvtrace_event_del;
+ rvtrace_pmu.module = THIS_MODULE;
+
+ return perf_pmu_register(&rvtrace_pmu, RVTRACE_PMU_NAME, -1);
+}
+
+void __exit rvtrace_perf_exit(void)
+{
+ perf_pmu_unregister(&rvtrace_pmu);
+}
diff --git a/include/linux/rvtrace.h b/include/linux/rvtrace.h
index 17d2fd9234c2..0a454707633b 100644
--- a/include/linux/rvtrace.h
+++ b/include/linux/rvtrace.h
@@ -335,4 +335,7 @@ static inline void rvtrace_unregister_driver(struct rvtrace_driver *rtdrv)
driver_unregister(&rtdrv->driver);
}
+int rvtrace_perf_init(void);
+void rvtrace_perf_exit(void);
+
#endif
--
2.43.0
next prev parent reply other threads:[~2025-10-02 6:11 UTC|newest]
Thread overview: 72+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-02 6:07 [PATCH 00/11] Linux RISC-V trace framework and drivers Anup Patel
2025-10-02 6:07 ` Anup Patel
2025-10-02 6:07 ` [PATCH 01/11] dt-bindings: Add RISC-V trace component bindings Anup Patel
2025-10-02 6:07 ` Anup Patel
2025-10-02 19:25 ` Rob Herring
2025-10-02 19:25 ` Rob Herring
2025-10-09 13:34 ` Anup Patel
2025-10-09 13:34 ` Anup Patel
2025-10-02 6:07 ` [PATCH 02/11] rvtrace: Initial implementation of driver framework Anup Patel
2025-10-02 6:07 ` Anup Patel
2025-10-11 1:07 ` Bo Gan
2025-10-11 1:07 ` Bo Gan
2025-10-30 8:37 ` Anup Patel
2025-10-30 8:37 ` Anup Patel
2026-01-21 7:50 ` Vincent Chen
2026-01-21 7:50 ` Vincent Chen
2026-01-26 13:36 ` Anup Patel
2026-01-26 13:36 ` Anup Patel
2026-01-27 12:19 ` Bo Gan
2026-01-27 12:19 ` Bo Gan
2026-03-13 2:10 ` Vincent Chen
2026-03-13 2:10 ` Vincent Chen
2025-10-02 6:07 ` [PATCH 03/11] rvtrace: Add functions to create/destroy a trace component path Anup Patel
2025-10-02 6:07 ` Anup Patel
2025-10-02 6:07 ` [PATCH 04/11] rvtrace: Add functions to start/stop tracing on a " Anup Patel
2025-10-02 6:07 ` Anup Patel
2025-10-08 9:13 ` Bo Gan
2025-10-08 9:13 ` Bo Gan
2025-10-13 3:43 ` Anup Patel
2025-10-13 3:43 ` Anup Patel
2025-10-13 4:52 ` Bo Gan
2025-10-13 4:52 ` Bo Gan
2025-10-14 8:10 ` Mayuresh Chitale
2025-10-14 8:10 ` Mayuresh Chitale
2025-10-14 8:59 ` Bo Gan
2025-10-14 8:59 ` Bo Gan
2025-10-02 6:07 ` [PATCH 05/11] rvtrace: Add trace encoder driver Anup Patel
2025-10-02 6:07 ` Anup Patel
2025-10-07 7:09 ` Bo Gan
2025-10-07 7:09 ` Bo Gan
2025-10-08 8:48 ` Bo Gan
2025-10-08 8:48 ` Bo Gan
[not found] ` <CAN37VV7uBkRzYsQcgGtw_iFg=za91OH7_1OSJ+b8eeuCzL5iDw@mail.gmail.com>
2025-10-08 9:51 ` Bo Gan
2025-10-08 9:51 ` Bo Gan
2025-10-02 6:07 ` [PATCH 06/11] rvtrace: Add function to copy into perf AUX buffer Anup Patel
2025-10-02 6:07 ` Anup Patel
2025-10-02 6:07 ` [PATCH 07/11] rvtrace: Add trace ramsink driver Anup Patel
2025-10-02 6:07 ` Anup Patel
2025-10-07 7:49 ` Bo Gan
2025-10-07 7:49 ` Bo Gan
[not found] ` <CAN37VV5J2+gzpraR2NhaJBNfQ3dPsr-72Mmg03+ykcLoouZ8_Q@mail.gmail.com>
2025-10-11 0:41 ` Bo Gan
2025-10-11 0:41 ` Bo Gan
2025-10-13 13:38 ` Mayuresh Chitale
2025-10-13 13:38 ` Mayuresh Chitale
2025-10-02 6:07 ` Anup Patel [this message]
2025-10-02 6:07 ` [PATCH 08/11] rvtrace: Add perf driver for tracing using perf tool Anup Patel
2025-10-02 6:07 ` [PATCH 09/11] perf tools: Add RISC-V trace PMU record capabilities Anup Patel
2025-10-02 6:07 ` Anup Patel
2025-10-02 6:07 ` [PATCH 10/11] perf tools: Initial support for RISC-V trace decoder Anup Patel
2025-10-02 6:07 ` Anup Patel
2025-10-02 6:07 ` [PATCH 11/11] MAINTAINERS: Add entry for RISC-V trace framework and drivers Anup Patel
2025-10-02 6:07 ` Anup Patel
2025-10-02 6:26 ` [PATCH 00/11] Linux " Greg KH
2025-10-02 6:26 ` Greg KH
2025-10-02 6:39 ` Anup Patel
2025-10-02 6:39 ` Anup Patel
2025-10-02 6:44 ` Greg KH
2025-10-02 6:44 ` Greg KH
2025-10-02 20:42 ` Bo Gan
2025-10-02 20:42 ` Bo Gan
2025-10-03 4:15 ` Anup Patel
2025-10-03 4:15 ` Anup Patel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251002060732.100213-9-apatel@ventanamicro.com \
--to=apatel@ventanamicro.com \
--cc=adrian.hunter@intel.com \
--cc=ajones@ventanamicro.com \
--cc=alex@ghiti.fr \
--cc=alexander.shishkin@linux.intel.com \
--cc=anup@brainfault.org \
--cc=atish.patra@linux.dev \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=gregkh@linuxfoundation.org \
--cc=irogers@google.com \
--cc=jolsa@kernel.org \
--cc=kan.liang@linux.intel.com \
--cc=krzk+dt@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-riscv@lists.infradead.org \
--cc=mark.rutland@arm.com \
--cc=mchitale@gmail.com \
--cc=mchitale@ventanamicro.com \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=palmer@dabbelt.com \
--cc=paul.walmsley@sifive.com \
--cc=peterz@infradead.org \
--cc=robh@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.