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 v2 09/12] rvtrace: Add perf driver for tracing using perf tool
Date: Sat, 1 Nov 2025 21:12:42 +0530 [thread overview]
Message-ID: <20251101154245.162492-10-apatel@ventanamicro.com> (raw)
In-Reply-To: <20251101154245.162492-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 0577f9acb858..ba11acf1117d 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 b955e5f3b048..bbe39aaf930d 100644
--- a/drivers/hwtracing/rvtrace/rvtrace-core.c
+++ b/drivers/hwtracing/rvtrace/rvtrace-core.c
@@ -777,11 +777,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 cecf6c153ca6..d5f782ac132f 100644
--- a/include/linux/rvtrace.h
+++ b/include/linux/rvtrace.h
@@ -343,4 +343,7 @@ static inline int rvtrace_comp_is_empty(struct rvtrace_component *comp)
comp->pdata->control_poll_timeout_usecs);
}
+int rvtrace_perf_init(void);
+void rvtrace_perf_exit(void);
+
#endif
--
2.43.0
next prev parent reply other threads:[~2025-11-01 15:44 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-01 15:42 [PATCH v2 00/12] Linux RISC-V trace framework and drivers Anup Patel
2025-11-01 15:42 ` [PATCH v2 01/12] dt-bindings: Add RISC-V trace component bindings Anup Patel
2025-11-20 16:39 ` Rob Herring
2026-01-02 3:38 ` Anup Patel
2025-11-01 15:42 ` [PATCH v2 02/12] rvtrace: Initial implementation of driver framework Anup Patel
2025-11-21 7:49 ` Nutty.Liu
2025-12-02 10:41 ` Bo Gan
2025-11-01 15:42 ` [PATCH v2 03/12] rvtrace: Add functions to create/destroy a trace component path Anup Patel
2025-11-01 15:42 ` [PATCH v2 04/12] rvtrace: Add functions to start/stop tracing on a " Anup Patel
2025-11-01 15:42 ` [PATCH v2 05/12] rvtrace: Add trace encoder driver Anup Patel
2025-11-01 15:42 ` [PATCH v2 06/12] rvtrace: Add function to copy into perf AUX buffer Anup Patel
2025-11-01 15:42 ` [PATCH v2 07/12] rvtrace: Add trace ramsink driver Anup Patel
2025-11-30 7:45 ` Bo Gan
2025-12-02 11:47 ` Mayuresh Chitale
2025-12-03 1:10 ` Bo Gan
2025-12-06 17:59 ` Mayuresh Chitale
2025-11-01 15:42 ` [PATCH v2 08/12] riscv: Enable DMA_RESTRICTED_POOL in defconfig Anup Patel
2025-11-01 15:42 ` Anup Patel [this message]
2026-02-06 13:07 ` [PATCH v2 09/12] rvtrace: Add perf driver for tracing using perf tool Eric Lin
2026-02-09 11:43 ` Mayuresh Chitale
2025-11-01 15:42 ` [PATCH v2 10/12] perf tools: Add RISC-V trace PMU record capabilities Anup Patel
2025-11-21 8:09 ` Nutty.Liu
2026-01-02 3:52 ` Anup Patel
2026-02-06 11:22 ` Eric Lin
2026-02-09 11:47 ` Mayuresh Chitale
2025-11-01 15:42 ` [PATCH v2 11/12] perf tools: Initial support for RISC-V trace decoder Anup Patel
2026-02-06 12:55 ` Eric Lin
2026-02-09 11:56 ` Mayuresh Chitale
2025-11-01 15:42 ` [PATCH v2 12/12] MAINTAINERS: Add entry for RISC-V trace framework and drivers 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=20251101154245.162492-10-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 \
--cc=sunilvl@ventanamicro.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox