public inbox for linux-riscv@lists.infradead.org
 help / color / mirror / Atom feed
* [PATCH v1 1/3] iommu/riscv: Add iommu perf driver
@ 2025-09-15  2:09 Yaxing Guo
  2025-09-15  2:09 ` [PATCH v1 2/3] iommu/riscv: Add Makefile support for RISC-V IOMMU " Yaxing Guo
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Yaxing Guo @ 2025-09-15  2:09 UTC (permalink / raw)
  To: linux-riscv
  Cc: iommu, tjeznach, joro, will, robin.murphy, paul.walmsley, palmer,
	aou, alex, anxu, wangran, Yaxing Guo

This patch introduces a performance monitor driver for RISC-V IOMMU
iohpm implementions that support hardware performance counters.
The driver expose RISC-V-IOMMU-specific performance events (eg. cycles,
tlb miss ...)through the Linux perf subsystem, enabling developers
to profile and optimize I/O translation performance.

Key features:
- Implements a perf PMU driver for RISC-V IOMMU.

- Exposes IOMMU perf events, such as 'cycles', 'tlb_miss'... via
  /sys/devices/riscv-iommu-pmu/events/, allowing use with perf
  tools:
    perf stat -C 0 -e riscv-iommu-pmu/cycles/ ...
    perf stat -C 0 -e riscv-iommu-pmu/tlb_miss/ ...
    ...
- Supports event filtering through configureable attributes exposed
  in /sys/devices/riscv-iommu-pmu/format/, including:
  dv_gscv, pv_pscv,did_gscid,pid_pscid,idt (riscv iommu spec 5-23).

- Implements overflow interrupt handling.

- Adds device tree binding support via optional 'pmu-name' property
  allowing platform-specific IOMMU implementations to specify a custom
  defined event ID list. If not provided, the driver defaults to only
  supporting 'cycles'. When set to 'dummy', it enables all 9 standard
  event IDs as defined in riscv iommu spec(5-23).

Signed-off-by: Yaxing Guo <guoyaxing@bosc.ac.cn>
---
 drivers/iommu/riscv/iommu-perf.c | 535 +++++++++++++++++++++++++++++++
 drivers/iommu/riscv/iommu-perf.h |  88 +++++
 drivers/iommu/riscv/iommu.h      |   8 +
 3 files changed, 631 insertions(+)
 create mode 100644 drivers/iommu/riscv/iommu-perf.c
 create mode 100644 drivers/iommu/riscv/iommu-perf.h

diff --git a/drivers/iommu/riscv/iommu-perf.c b/drivers/iommu/riscv/iommu-perf.c
new file mode 100644
index 000000000000..a9a8788a5776
--- /dev/null
+++ b/drivers/iommu/riscv/iommu-perf.c
@@ -0,0 +1,535 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * IOMMU hpm implementations
+ * Copyright(c) 2025 Beijing Institute of Open Source Chip (BOSC)
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/perf_event.h>
+#include <linux/iommu.h>
+#include "iommu.h"
+#include "iommu-perf.h"
+
+#define EVENT_CYCLES              0
+#define EVENT_UNTRANSLATED_REQ    1
+#define EVENT_TRANSLATED_REQUESTS 2
+#define EVENT_ATS_TRANSLATION_REQ 3
+#define EVENT_TLB_MISS            4
+#define EVENT_DDT_WALK            5
+#define EVENT_PDT_WALK            6
+#define EVENT_STAGE1_PT_WALK      7
+#define EVENT_STAGE2_PT_WALK      8
+
+RISCV_IOMMU_PMU_EXT_EVENT_ATTR(translated_req,      "config=0x001")
+RISCV_IOMMU_PMU_EXT_EVENT_ATTR(untranslated_req,    "config=0x002")
+RISCV_IOMMU_PMU_EXT_EVENT_ATTR(ats_translation_req, "config=0x003")
+RISCV_IOMMU_PMU_EXT_EVENT_ATTR(tlb_miss,            "config=0x004")
+RISCV_IOMMU_PMU_EXT_EVENT_ATTR(ddt_walk,            "config=0x005")
+RISCV_IOMMU_PMU_EXT_EVENT_ATTR(pdt_walk,            "config=0x006")
+RISCV_IOMMU_PMU_EXT_EVENT_ATTR(stage1_pt_walk,      "config=0x007")
+RISCV_IOMMU_PMU_EXT_EVENT_ATTR(stage2_pt_walk,      "config=0x008")
+
+static const struct attribute_group *riscv_iommu_pmu_attr_update_dummy[] = {
+	&translated_req,
+	&untranslated_req,
+	&ats_translation_req,
+	&tlb_miss,
+	&ddt_walk,
+	&pdt_walk,
+	&stage1_pt_walk,
+	&stage2_pt_walk,
+	NULL
+};
+
+static struct riscv_iommu_pmu_event_map event_map[] = {
+	{ "dummy", riscv_iommu_pmu_attr_update_dummy },
+	{ NULL,    NULL}
+};
+
+PMU_EVENT_ATTR_STRING(pv_pscv,   format_pv_pscv_attr,   "config1:0");
+PMU_EVENT_ATTR_STRING(dv_gscv,   format_dv_gscv_attr,   "config1:1");
+PMU_EVENT_ATTR_STRING(idt,       format_idt_attr,       "config1:2");
+PMU_EVENT_ATTR_STRING(pid_pscid, format_pid_pscid_attr, "config1:20-39");
+PMU_EVENT_ATTR_STRING(did_gscid, format_did_gscid_attr, "config1:40-63");
+
+static struct attribute *formats_attrs[] = {
+	&format_pv_pscv_attr.attr.attr,
+	&format_dv_gscv_attr.attr.attr,
+	&format_idt_attr.attr.attr,
+	&format_pid_pscid_attr.attr.attr,
+	&format_did_gscid_attr.attr.attr,
+	NULL
+};
+
+static struct attribute_group riscv_iommu_pmu_format_attr_group = {
+	.name = "format",
+	.attrs = formats_attrs,
+};
+
+PMU_EVENT_ATTR_STRING(cycles, event_cycles_attr, "config=0x0");
+
+static struct attribute *events_attrs[] = {
+	&event_cycles_attr.attr.attr,
+	NULL
+};
+
+static struct attribute_group riscv_iommu_pmu_events_attr_group = {
+	.name = "events",
+	.attrs = events_attrs,
+};
+
+static const struct attribute_group *riscv_iommu_pmu_attr_groups[] = {
+	&riscv_iommu_pmu_format_attr_group,
+	&riscv_iommu_pmu_events_attr_group,
+	NULL,
+};
+
+static void __riscv_iommu_pmu_unregister(struct riscv_iommu_pmu *iommu_pmu);
+static void riscv_iommu_pmu_read(struct perf_event *event);
+
+static void riscv_iommu_pmu_hpmevt_set(iohpmevt_t *hpmevt, unsigned long event_id,
+				       int pv_pscv, int dv_gscv, int idt,
+				       int pid_pscid, int did_gscid, int of)
+{
+	hpmevt->val = 0;
+	hpmevt->eventID = event_id;
+	hpmevt->IDT = idt;
+	hpmevt->OF = of;
+	hpmevt->PID_PSCID = pid_pscid;
+	hpmevt->DID_GSCID = did_gscid;
+	hpmevt->PV_PSCV = pv_pscv;
+	hpmevt->DV_GSCV = dv_gscv;
+}
+
+static struct riscv_iommu_perf_event *
+get_riscv_iommu_perf_event(struct riscv_iommu_pmu *iommu_pmu,
+			   struct perf_event *event,
+			   int pv_pscv, int dv_gscv, int idt,
+			   int pid_pscid, int did_gscid,
+			   int *idx)
+{
+	int i, nr;
+	struct riscv_iommu_device *iommu = iommu_pmu->iommu;
+	struct riscv_iommu_perf_event *iommu_event;
+
+	for (i = 0; i < RISCV_IOMMU_IOHPMCTR_CNT; i++) {
+		iommu_event = iommu->events[i];
+		if (iommu_event == NULL)
+			continue;
+
+		if (iommu_event->perf_event == event) {
+			nr = i;
+			goto update;
+		}
+	}
+
+	nr = find_first_zero_bit(&iommu->iohpmctr_bitmap,
+				 RISCV_IOMMU_IOHPMCTR_CNT);
+	if (nr >= RISCV_IOMMU_IOHPMCTR_CNT)
+		return NULL;
+again:
+	if (test_and_set_bit(nr, &iommu_pmu->iommu->iohpmctr_bitmap))
+		goto again;
+
+	iommu_event = kzalloc(sizeof(struct riscv_iommu_perf_event), GFP_KERNEL);
+	if (!iommu_event)
+		return NULL;
+update:
+	iommu_event->perf_event = event;
+	iommu_event->pv_pscv = pv_pscv;
+	iommu_event->dv_gscv = dv_gscv;
+	iommu_event->idt = idt;
+	iommu_event->pid_pscid = pid_pscid;
+	iommu_event->did_gscid = did_gscid;
+	iommu->events[nr] = iommu_event;
+
+	*idx = nr;
+
+	return iommu_event;
+}
+
+static int riscv_iommu_pmu_event_add(struct riscv_iommu_pmu *iommu_pmu,
+				     struct perf_event *event)
+{
+	int nr = -1, of;
+	unsigned long event_id = event->attr.config;
+	riscv_iommu_pmu_cfg1_t config1;
+	struct hw_perf_event *hwc = &event->hw;
+	struct riscv_iommu_device *iommu = iommu_pmu->iommu;
+
+	config1.val = event->attr.config1;
+
+	if (event_id >= RISCV_IOMMU_IOHPMCTR_CNT)
+		return -EINVAL;
+
+	if (iommu->hpm_irq)
+		of = 0;
+	else
+		of = 1;
+
+	if (event_id == EVENT_CYCLES) {
+		unsigned long val;
+
+		val = riscv_iommu_readq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMCYCLES);
+		if (of)
+			val &= ~RISCV_IOMMU_IOHPMCYCLES_OF;
+		else
+			val |= RISCV_IOMMU_IOHPMCYCLES_OF;
+		riscv_iommu_writeq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMCYCLES, val);
+
+		hwc->idx = 0;
+		iommu->events[0]->perf_event = event;
+	} else {
+		struct riscv_iommu_perf_event *iommu_perf_event;
+
+		iommu_perf_event = get_riscv_iommu_perf_event(iommu_pmu, event,
+							      config1.pv_pscv, config1.dv_gscv,
+							      config1.idt, config1.pid_pscid,
+							      config1.did_gscid, &nr);
+		if (!iommu_perf_event)
+			return -ENOSPC;
+
+		riscv_iommu_pmu_hpmevt_set(&iommu_pmu->iommu->iohpmevt[nr], event_id,
+					   iommu_perf_event->pv_pscv, iommu_perf_event->dv_gscv,
+					   iommu_perf_event->idt, iommu_perf_event->pid_pscid,
+					   iommu_perf_event->did_gscid, of);
+		riscv_iommu_writeq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMEVT(nr),
+				   iommu_pmu->iommu->iohpmevt[nr].val);
+
+		hwc->idx = nr;
+	}
+
+	return 0;
+}
+
+static int riscv_iommu_pmu_hpmevt_idx_get(struct riscv_iommu_pmu *iommu_pmu, int event_id)
+{
+	int i;
+	iohpmevt_t *iohpmevt;
+
+	for (i = 0; i < RISCV_IOMMU_IOHPMCTR_CNT; i++) {
+		iohpmevt = &iommu_pmu->iommu->iohpmevt[i];
+		if (iohpmevt->eventID == event_id)
+			return iohpmevt - iommu_pmu->iommu->iohpmevt;
+	}
+
+	return -1;
+}
+
+static int riscv_iommu_event_del(struct riscv_iommu_pmu *iommu_pmu,
+				 struct perf_event *event)
+{
+	unsigned long config = event->attr.config;
+	struct riscv_iommu_device *iommu = iommu_pmu->iommu;
+
+	if (config >= RISCV_IOMMU_IOHPMCTR_CNT)
+		return -EINVAL;
+
+	if (config == EVENT_CYCLES) {
+		iommu->events[0] = NULL;
+	} else {
+		int nr;
+
+		nr = riscv_iommu_pmu_hpmevt_idx_get(iommu_pmu, config);
+		if (-1 == nr)
+			return -1;
+		riscv_iommu_pmu_hpmevt_set(&iommu_pmu->iommu->iohpmevt[nr], 0,
+					   0, 0, 0, 0, 0, 0);
+		clear_bit(nr, &iommu_pmu->iommu->iohpmctr_bitmap);
+		riscv_iommu_writeq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMEVT(nr),
+				   0);
+		kfree(iommu->events[nr]);
+		iommu->events[nr] = NULL;
+	}
+
+	return 0;
+}
+
+static int riscv_iommu_pmu_event_init(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+
+	if (event->attr.sample_period)
+		return -EINVAL;
+
+	if (event->cpu < 0)
+		return -EINVAL;
+
+	hwc->config = event->attr.config;
+
+	return 0;
+}
+
+static void riscv_iommu_pmu_enable(struct pmu *pmu)
+{
+}
+
+static void riscv_iommu_pmu_disable(struct pmu *pmu)
+{
+}
+
+static void riscv_iommu_pmu_start(struct perf_event *event, int flags)
+{
+	struct riscv_iommu_pmu *iommu_pmu = riscv_iommu_event_to_pmu(event);
+	struct hw_perf_event *hwc = &event->hw;
+	unsigned long count;
+
+	hwc->state = 0;
+
+	if (hwc->idx == EVENT_CYCLES)
+		count = riscv_iommu_readq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMCYCLES);
+	else
+		count = riscv_iommu_readq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMCTR(hwc->idx));
+
+	local64_set((&hwc->prev_count), count);
+
+	perf_event_update_userpage(event);
+}
+
+static void riscv_iommu_pmu_stop(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+
+	if (!(hwc->state & PERF_HES_STOPPED)) {
+		riscv_iommu_pmu_read(event);
+		hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
+	}
+}
+
+static int riscv_iommu_pmu_add(struct perf_event *event, int flags)
+{
+	struct riscv_iommu_pmu *iommu_pmu = riscv_iommu_event_to_pmu(event);
+	struct hw_perf_event *hwc = &event->hw;
+
+	riscv_iommu_pmu_event_add(iommu_pmu, event);
+
+	hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+
+	if (flags & PERF_EF_START)
+		riscv_iommu_pmu_start(event, 0);
+
+	return 0;
+}
+
+static void riscv_iommu_pmu_del(struct perf_event *event, int flags)
+{
+	struct riscv_iommu_pmu *iommu_pmu = riscv_iommu_event_to_pmu(event);
+
+	riscv_iommu_pmu_stop(event, PERF_EF_UPDATE);
+
+	riscv_iommu_event_del(iommu_pmu, event);
+	event->hw.idx = -1;
+
+	perf_event_update_userpage(event);
+}
+
+static void riscv_iommu_pmu_read(struct perf_event *event)
+{
+	struct riscv_iommu_pmu *iommu_pmu = riscv_iommu_event_to_pmu(event);
+	struct hw_perf_event *hwc = &event->hw;
+	unsigned long prev_count, new_count, delta;
+
+again:
+	prev_count = local64_read(&hwc->prev_count);
+	if (hwc->idx == EVENT_CYCLES)
+		new_count = riscv_iommu_readq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMCYCLES);
+	else
+		new_count = riscv_iommu_readq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMCTR(hwc->idx));
+
+	if (local64_xchg(&hwc->prev_count, new_count) != prev_count)
+		goto again;
+	delta = new_count - prev_count;
+
+	local64_add(delta, &event->count);
+}
+
+int riscv_iommu_pmu_alloc(struct riscv_iommu_device *iommu)
+{
+	struct riscv_iommu_pmu *iommu_pmu;
+	struct riscv_iommu_perf_event *iommu_event;
+	int ret = 0;
+
+	if (iommu->pmu)
+		return -EEXIST;
+
+	iommu_pmu = kzalloc(sizeof(struct riscv_iommu_pmu), GFP_KERNEL);
+	if (!iommu_pmu)
+		return -ENOMEM;
+
+	iommu_pmu->iommu = iommu;
+	iommu->pmu = iommu_pmu;
+
+	set_bit(0, &iommu_pmu->iommu->iohpmctr_bitmap);
+	iommu_event = kzalloc(sizeof(struct riscv_iommu_perf_event), GFP_KERNEL);
+	if (!iommu_event) {
+		ret = -ENOMEM;
+		goto free_pmu;
+	}
+	iommu->events[0] = iommu_event;
+
+	return 0;
+
+free_pmu:
+	kfree(iommu_pmu);
+	return ret;
+}
+
+static void riscv_iommu_pmu_do_overflow(struct riscv_iommu_device *iommu)
+{
+	int idx;
+	struct riscv_iommu_perf_event *iommu_event;
+
+	for_each_set_bit(idx, &iommu->iohpmctr_bitmap,
+			 RISCV_IOMMU_IOHPMCTR_CNT) {
+		iohpmevt_t hpmevt;
+		unsigned int val;
+
+		if (idx == 0)
+			continue;
+		iommu_event = iommu->events[idx];
+		if (!iommu_event)
+			continue;
+		hpmevt.val = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_IOHPMEVT(idx));
+		if (hpmevt.OF) {
+			riscv_iommu_pmu_read(iommu_event->perf_event);
+
+			hpmevt.OF = 0;
+			riscv_iommu_writeq(iommu, RISCV_IOMMU_REG_IOHPMEVT(idx), hpmevt.val);
+
+			val = riscv_iommu_readl(iommu, RISCV_IOMMU_REG_IPSR);
+			val &= ~RISCV_IOMMU_IPSR_PMIP;
+			riscv_iommu_writel(iommu, RISCV_IOMMU_REG_IPSR, val);
+		}
+	}
+}
+
+static irqreturn_t riscv_iommu_pmu_irq_handler(int irq, void *data)
+{
+	struct riscv_iommu_device *iommu = (struct riscv_iommu_device *)data;
+
+	riscv_iommu_pmu_do_overflow(iommu);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t riscv_iommu_pmu_ipsr(int irq, void *data)
+{
+	struct riscv_iommu_device *iommu = (struct riscv_iommu_device *)data;
+
+	if (riscv_iommu_readl(iommu, RISCV_IOMMU_REG_IPSR) & RISCV_IOMMU_IPSR_PMIP)
+		return IRQ_WAKE_THREAD;
+
+	return IRQ_NONE;
+}
+
+static int riscv_iommu_pmu_vec(struct riscv_iommu_device *iommu)
+{
+	return FIELD_GET(RISCV_IOMMU_ICVEC_PMIV, iommu->icvec);
+}
+
+static int riscv_iommu_pmu_set_irq(struct riscv_iommu_device *iommu)
+{
+	int irq, ret;
+
+	if (!iommu)
+		return -EINVAL;
+	irq = iommu->irqs[riscv_iommu_pmu_vec(iommu)];
+	if (!irq)
+		return -EEXIST;
+	iommu->hpm_irq = irq;
+
+	ret = request_threaded_irq(irq, riscv_iommu_pmu_ipsr,
+				   riscv_iommu_pmu_irq_handler,
+				   IRQF_ONESHOT, "rv-iommu-pmu-irq", iommu);
+	if (ret) {
+		iommu->hpm_irq = 0;
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct attribute_group **
+riscv_iommu_get_ext_attr(struct riscv_iommu_pmu *iommu_pmu)
+{
+	struct riscv_iommu_pmu_event_map *map = event_map;
+	const char *str;
+	struct device *dev = iommu_pmu->iommu->dev;
+
+	if (of_property_read_string(dev->of_node, "pmu-name", &str))
+		return NULL;
+
+	while (map->compatible) {
+		if (!strcmp(map->compatible, str))
+			return map->attr_group;
+	}
+
+	return NULL;
+}
+
+static int __riscv_iommu_pmu_register(struct riscv_iommu_pmu *iommu_pmu,
+				      const char *name)
+{
+	int ret;
+
+	if (!iommu_pmu)
+		return -EINVAL;
+
+	iommu_pmu->pmu.attr_groups = riscv_iommu_pmu_attr_groups;
+	iommu_pmu->pmu.attr_update = riscv_iommu_get_ext_attr(iommu_pmu);
+	iommu_pmu->pmu.task_ctx_nr = perf_invalid_context;
+	iommu_pmu->pmu.event_init  = riscv_iommu_pmu_event_init;
+	iommu_pmu->pmu.pmu_enable  = riscv_iommu_pmu_enable;
+	iommu_pmu->pmu.pmu_disable = riscv_iommu_pmu_disable;
+	iommu_pmu->pmu.add         = riscv_iommu_pmu_add;
+	iommu_pmu->pmu.del         = riscv_iommu_pmu_del;
+	iommu_pmu->pmu.start       = riscv_iommu_pmu_start;
+	iommu_pmu->pmu.stop        = riscv_iommu_pmu_stop;
+	iommu_pmu->pmu.read        = riscv_iommu_pmu_read;
+
+	ret = perf_pmu_register(&iommu_pmu->pmu, name, -1);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void __riscv_iommu_pmu_unregister(struct riscv_iommu_pmu *iommu_pmu)
+{
+	if (!iommu_pmu)
+		return;
+
+	perf_pmu_unregister(&iommu_pmu->pmu);
+
+	kfree(iommu_pmu->iommu->events[0]);
+	kfree(iommu_pmu);
+}
+
+int riscv_iommu_pmu_register(struct riscv_iommu_device *iommu)
+{
+	int ret;
+
+	ret = __riscv_iommu_pmu_register(iommu->pmu, "riscv-iommu-pmu");
+	if (ret)
+		goto err;
+
+	ret = riscv_iommu_pmu_set_irq(iommu);
+	if (ret)
+		goto unregister;
+
+	return 0;
+
+unregister:
+	riscv_iommu_pmu_unregister(iommu);
+err:
+	return ret;
+}
+
+void riscv_iommu_pmu_unregister(struct riscv_iommu_device *iommu)
+{
+	__riscv_iommu_pmu_unregister(iommu->pmu);
+
+	iommu->pmu = NULL;
+}
diff --git a/drivers/iommu/riscv/iommu-perf.h b/drivers/iommu/riscv/iommu-perf.h
new file mode 100644
index 000000000000..5c4cc7b9a978
--- /dev/null
+++ b/drivers/iommu/riscv/iommu-perf.h
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * IOMMU hpm implementations
+ * Copyright(c) 2025 Beijing Institute of Open Source Chip (BOSC)
+ */
+
+#ifndef __IOMMU_PERF_H__
+#define __IOMMU_PERF_H__
+
+#include <linux/sysfs.h>
+#include <linux/perf_event.h>
+#include <linux/bitops.h>
+
+typedef union {
+	struct {
+		unsigned long eventID:15;
+		unsigned long DMASK:1;
+		unsigned long PID_PSCID:20;
+		unsigned long DID_GSCID:24;
+		unsigned long PV_PSCV:1;
+		unsigned long DV_GSCV:1;
+		unsigned long IDT:1;
+		unsigned long OF:1;
+	};
+	unsigned long val;
+} iohpmevt_t;
+
+typedef union {
+	struct {
+		unsigned long pv_pscv:1;
+		unsigned long dv_gscv:1;
+		unsigned long idt:1;
+		unsigned long reserved:17;
+		unsigned long pid_pscid:20;
+		unsigned long did_gscid:24;
+	};
+	unsigned long val;
+} riscv_iommu_pmu_cfg1_t;
+
+struct riscv_iommu_pmu_event_map {
+	const char *compatible;
+	const struct attribute_group **attr_group;
+};
+
+#define RISCV_IOMMU_IOHPMCTR_CNT 32
+
+struct riscv_iommu_perf_event {
+	int pv_pscv;
+	int dv_gscv;
+	int idt;
+	int pid_pscid;
+	int did_gscid;
+	struct perf_event *perf_event;
+};
+
+struct riscv_iommu_pmu {
+	struct riscv_iommu_device *iommu;
+	struct pmu pmu;
+};
+
+static inline struct riscv_iommu_pmu *dev_to_riscv_iommu_pmu(struct device *dev)
+{
+	return container_of(dev_get_drvdata(dev), struct riscv_iommu_pmu, pmu);
+}
+
+static inline struct riscv_iommu_pmu *riscv_iommu_event_to_pmu(struct perf_event *event)
+{
+	return container_of(event->pmu, struct riscv_iommu_pmu, pmu);
+}
+
+#define RISCV_IOMMU_PMU_EXT_EVENT_ATTR(_name, _string)			\
+	PMU_EVENT_ATTR_STRING(_name, event_attr_##_name, _string)	\
+									\
+static struct attribute *_name##_attr[] = {				\
+	&event_attr_##_name.attr.attr,					\
+	NULL								\
+};									\
+									\
+static struct attribute_group _name = {					\
+	.name		= "events",					\
+	.attrs		= _name##_attr,					\
+};
+
+int riscv_iommu_pmu_alloc(struct riscv_iommu_device *iommu);
+int riscv_iommu_pmu_register(struct riscv_iommu_device *iommu);
+void riscv_iommu_pmu_unregister(struct riscv_iommu_device *iommu);
+
+#endif
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 46df79dd5495..1eabe04dbbc1 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -14,8 +14,10 @@
 #include <linux/iommu.h>
 #include <linux/types.h>
 #include <linux/iopoll.h>
+#include <linux/perf_event.h>
 
 #include "iommu-bits.h"
+#include "iommu-perf.h"
 
 struct riscv_iommu_device;
 
@@ -60,6 +62,12 @@ struct riscv_iommu_device {
 	unsigned int ddt_mode;
 	dma_addr_t ddt_phys;
 	u64 *ddt_root;
+
+	iohpmevt_t iohpmevt[RISCV_IOMMU_IOHPMCTR_CNT];
+	unsigned long iohpmctr_bitmap;
+	struct riscv_iommu_pmu *pmu;
+	int hpm_irq;
+	struct riscv_iommu_perf_event *events[RISCV_IOMMU_IOHPMCTR_CNT];
 };
 
 int riscv_iommu_init(struct riscv_iommu_device *iommu);
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v1 2/3] iommu/riscv: Add Makefile support for RISC-V IOMMU perf driver
  2025-09-15  2:09 [PATCH v1 1/3] iommu/riscv: Add iommu perf driver Yaxing Guo
@ 2025-09-15  2:09 ` Yaxing Guo
  2025-09-16  3:40   ` kernel test robot
  2025-09-15  2:09 ` [PATCH v1 3/3] iommu/riscv: Register RISC-V IOMMU PMU at init time Yaxing Guo
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 10+ messages in thread
From: Yaxing Guo @ 2025-09-15  2:09 UTC (permalink / raw)
  To: linux-riscv
  Cc: iommu, tjeznach, joro, will, robin.murphy, paul.walmsley, palmer,
	aou, alex, anxu, wangran, Yaxing Guo

Add compilation rules for iommu-perf.o to support
the new RISC-V IOMMU perf driver.

Signed-off-by: Yaxing Guo <guoyaxing@bosc.ac.cn>
---
 drivers/iommu/riscv/Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/iommu/riscv/Makefile b/drivers/iommu/riscv/Makefile
index b5929f9f23e6..5e9c7c1ed928 100644
--- a/drivers/iommu/riscv/Makefile
+++ b/drivers/iommu/riscv/Makefile
@@ -1,3 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0-only
-obj-y += iommu.o iommu-platform.o
+obj-y += iommu.o iommu-platform.o iommu-perf.o
 obj-$(CONFIG_RISCV_IOMMU_PCI) += iommu-pci.o
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v1 3/3] iommu/riscv: Register RISC-V IOMMU PMU at init time
  2025-09-15  2:09 [PATCH v1 1/3] iommu/riscv: Add iommu perf driver Yaxing Guo
  2025-09-15  2:09 ` [PATCH v1 2/3] iommu/riscv: Add Makefile support for RISC-V IOMMU " Yaxing Guo
@ 2025-09-15  2:09 ` Yaxing Guo
  2025-09-15 10:38 ` [PATCH v1 1/3] iommu/riscv: Add iommu perf driver Will Deacon
  2025-09-16  1:47 ` kernel test robot
  3 siblings, 0 replies; 10+ messages in thread
From: Yaxing Guo @ 2025-09-15  2:09 UTC (permalink / raw)
  To: linux-riscv
  Cc: iommu, tjeznach, joro, will, robin.murphy, paul.walmsley, palmer,
	aou, alex, anxu, wangran, Yaxing Guo

Register the RISC-V IOMMU driver during IOMMU driver
initialization.

Signed-off-by: Yaxing Guo <guoyaxing@bosc.ac.cn>
---
 drivers/iommu/riscv/iommu.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 0eae2f4bdc5e..453d04b03c6c 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -23,6 +23,7 @@
 #include "../iommu-pages.h"
 #include "iommu-bits.h"
 #include "iommu.h"
+#include "iommu-perf.h"
 
 /* Timeouts in [us] */
 #define RISCV_IOMMU_QCSR_TIMEOUT	150000
@@ -1604,6 +1605,7 @@ void riscv_iommu_remove(struct riscv_iommu_device *iommu)
 	riscv_iommu_iodir_set_mode(iommu, RISCV_IOMMU_DDTP_IOMMU_MODE_OFF);
 	riscv_iommu_queue_disable(&iommu->cmdq);
 	riscv_iommu_queue_disable(&iommu->fltq);
+	riscv_iommu_pmu_unregister(iommu);
 }
 
 int riscv_iommu_init(struct riscv_iommu_device *iommu)
@@ -1656,6 +1658,14 @@ int riscv_iommu_init(struct riscv_iommu_device *iommu)
 		goto err_remove_sysfs;
 	}
 
+	rc = riscv_iommu_pmu_alloc(iommu);
+	if (rc) {
+		dev_err(iommu->dev, "cannot alloc iommu pmu (%d)\n", rc);
+		iommu_device_sysfs_remove(&iommu->iommu);
+		goto err_remove_sysfs;
+	}
+	riscv_iommu_pmu_register(iommu);
+
 	return 0;
 
 err_remove_sysfs:
-- 
2.34.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v1 1/3] iommu/riscv: Add iommu perf driver
  2025-09-15  2:09 [PATCH v1 1/3] iommu/riscv: Add iommu perf driver Yaxing Guo
  2025-09-15  2:09 ` [PATCH v1 2/3] iommu/riscv: Add Makefile support for RISC-V IOMMU " Yaxing Guo
  2025-09-15  2:09 ` [PATCH v1 3/3] iommu/riscv: Register RISC-V IOMMU PMU at init time Yaxing Guo
@ 2025-09-15 10:38 ` Will Deacon
  2025-09-16 10:27   ` guoyaxing
  2025-09-16  1:47 ` kernel test robot
  3 siblings, 1 reply; 10+ messages in thread
From: Will Deacon @ 2025-09-15 10:38 UTC (permalink / raw)
  To: Yaxing Guo
  Cc: linux-riscv, iommu, tjeznach, joro, robin.murphy, paul.walmsley,
	palmer, aou, alex, anxu, wangran

On Mon, Sep 15, 2025 at 10:09:09AM +0800, Yaxing Guo wrote:
> This patch introduces a performance monitor driver for RISC-V IOMMU
> iohpm implementions that support hardware performance counters.
> The driver expose RISC-V-IOMMU-specific performance events (eg. cycles,
> tlb miss ...)through the Linux perf subsystem, enabling developers
> to profile and optimize I/O translation performance.
> 
> Key features:
> - Implements a perf PMU driver for RISC-V IOMMU.
> 
> - Exposes IOMMU perf events, such as 'cycles', 'tlb_miss'... via
>   /sys/devices/riscv-iommu-pmu/events/, allowing use with perf
>   tools:
>     perf stat -C 0 -e riscv-iommu-pmu/cycles/ ...
>     perf stat -C 0 -e riscv-iommu-pmu/tlb_miss/ ...
>     ...
> - Supports event filtering through configureable attributes exposed
>   in /sys/devices/riscv-iommu-pmu/format/, including:
>   dv_gscv, pv_pscv,did_gscid,pid_pscid,idt (riscv iommu spec 5-23).
> 
> - Implements overflow interrupt handling.
> 
> - Adds device tree binding support via optional 'pmu-name' property
>   allowing platform-specific IOMMU implementations to specify a custom
>   defined event ID list. If not provided, the driver defaults to only
>   supporting 'cycles'. When set to 'dummy', it enables all 9 standard
>   event IDs as defined in riscv iommu spec(5-23).
> 
> Signed-off-by: Yaxing Guo <guoyaxing@bosc.ac.cn>
> ---
>  drivers/iommu/riscv/iommu-perf.c | 535 +++++++++++++++++++++++++++++++
>  drivers/iommu/riscv/iommu-perf.h |  88 +++++
>  drivers/iommu/riscv/iommu.h      |   8 +
>  3 files changed, 631 insertions(+)
>  create mode 100644 drivers/iommu/riscv/iommu-perf.c
>  create mode 100644 drivers/iommu/riscv/iommu-perf.h

PMU drivers are better placed under drivers/perf/

(the Arm SMMUv3 PMU driver lives there, for example).

Will

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v1 1/3] iommu/riscv: Add iommu perf driver
  2025-09-15  2:09 [PATCH v1 1/3] iommu/riscv: Add iommu perf driver Yaxing Guo
                   ` (2 preceding siblings ...)
  2025-09-15 10:38 ` [PATCH v1 1/3] iommu/riscv: Add iommu perf driver Will Deacon
@ 2025-09-16  1:47 ` kernel test robot
  3 siblings, 0 replies; 10+ messages in thread
From: kernel test robot @ 2025-09-16  1:47 UTC (permalink / raw)
  To: Yaxing Guo, linux-riscv
  Cc: llvm, oe-kbuild-all, iommu, tjeznach, joro, will, robin.murphy,
	paul.walmsley, palmer, aou, alex, anxu, wangran, Yaxing Guo

Hi Yaxing,

kernel test robot noticed the following build errors:

[auto build test ERROR on linus/master]
[also build test ERROR on v6.17-rc6 next-20250912]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Yaxing-Guo/iommu-riscv-Add-Makefile-support-for-RISC-V-IOMMU-perf-driver/20250915-101640
base:   linus/master
patch link:    https://lore.kernel.org/r/20250915020911.1313-1-guoyaxing%40bosc.ac.cn
patch subject: [PATCH v1 1/3] iommu/riscv: Add iommu perf driver
config: riscv-randconfig-001-20250916 (https://download.01.org/0day-ci/archive/20250916/202509160926.OINmzlTz-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 65ad21d730d25789454d18e811f8ff5db79cb5d4)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250916/202509160926.OINmzlTz-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202509160926.OINmzlTz-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from drivers/iommu/riscv/iommu-pci.c:21:
   In file included from drivers/iommu/riscv/iommu.h:20:
>> drivers/iommu/riscv/iommu-perf.h:68:29: error: no member named 'pmu' in 'struct perf_event'
      68 |         return container_of(event->pmu, struct riscv_iommu_pmu, pmu);
         |                             ~~~~~  ^
>> drivers/iommu/riscv/iommu-perf.h:68:29: error: no member named 'pmu' in 'struct perf_event'
      68 |         return container_of(event->pmu, struct riscv_iommu_pmu, pmu);
         |                             ~~~~~  ^
>> drivers/iommu/riscv/iommu-perf.h:68:29: error: no member named 'pmu' in 'struct perf_event'
      68 |         return container_of(event->pmu, struct riscv_iommu_pmu, pmu);
         |                             ~~~~~  ^
   3 errors generated.


vim +68 drivers/iommu/riscv/iommu-perf.h

    65	
    66	static inline struct riscv_iommu_pmu *riscv_iommu_event_to_pmu(struct perf_event *event)
    67	{
  > 68		return container_of(event->pmu, struct riscv_iommu_pmu, pmu);
    69	}
    70	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v1 2/3] iommu/riscv: Add Makefile support for RISC-V IOMMU perf driver
  2025-09-15  2:09 ` [PATCH v1 2/3] iommu/riscv: Add Makefile support for RISC-V IOMMU " Yaxing Guo
@ 2025-09-16  3:40   ` kernel test robot
  0 siblings, 0 replies; 10+ messages in thread
From: kernel test robot @ 2025-09-16  3:40 UTC (permalink / raw)
  To: Yaxing Guo, linux-riscv
  Cc: llvm, oe-kbuild-all, iommu, tjeznach, joro, will, robin.murphy,
	paul.walmsley, palmer, aou, alex, anxu, wangran, Yaxing Guo

Hi Yaxing,

kernel test robot noticed the following build errors:

[auto build test ERROR on linus/master]
[also build test ERROR on v6.17-rc6 next-20250915]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Yaxing-Guo/iommu-riscv-Add-Makefile-support-for-RISC-V-IOMMU-perf-driver/20250915-101640
base:   linus/master
patch link:    https://lore.kernel.org/r/20250915020911.1313-2-guoyaxing%40bosc.ac.cn
patch subject: [PATCH v1 2/3] iommu/riscv: Add Makefile support for RISC-V IOMMU perf driver
:::::: branch date: 25 hours ago
:::::: commit date: 25 hours ago
config: riscv-randconfig-001-20250916 (https://download.01.org/0day-ci/archive/20250916/202509161121.SHWsStQM-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 65ad21d730d25789454d18e811f8ff5db79cb5d4)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250916/202509161121.SHWsStQM-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/r/202509161121.SHWsStQM-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from drivers/iommu/riscv/iommu-perf.c:12:
   In file included from drivers/iommu/riscv/iommu.h:20:
   drivers/iommu/riscv/iommu-perf.h:68:29: error: no member named 'pmu' in 'struct perf_event'
      68 |         return container_of(event->pmu, struct riscv_iommu_pmu, pmu);
         |                             ~~~~~  ^
   drivers/iommu/riscv/iommu-perf.h:68:29: error: no member named 'pmu' in 'struct perf_event'
      68 |         return container_of(event->pmu, struct riscv_iommu_pmu, pmu);
         |                             ~~~~~  ^
   drivers/iommu/riscv/iommu-perf.h:68:29: error: no member named 'pmu' in 'struct perf_event'
      68 |         return container_of(event->pmu, struct riscv_iommu_pmu, pmu);
         |                             ~~~~~  ^
>> drivers/iommu/riscv/iommu-perf.c:157:34: error: no member named 'attr' in 'struct perf_event'
     157 |         unsigned long event_id = event->attr.config;
         |                                  ~~~~~  ^
>> drivers/iommu/riscv/iommu-perf.c:159:38: error: no member named 'hw' in 'struct perf_event'
     159 |         struct hw_perf_event *hwc = &event->hw;
         |                                      ~~~~~  ^
   drivers/iommu/riscv/iommu-perf.c:162:23: error: no member named 'attr' in 'struct perf_event'
     162 |         config1.val = event->attr.config1;
         |                       ~~~~~  ^
>> drivers/iommu/riscv/iommu-perf.c:182:8: error: no member named 'idx' in 'struct hw_perf_event'
     182 |                 hwc->idx = 0;
         |                 ~~~  ^
   drivers/iommu/riscv/iommu-perf.c:201:8: error: no member named 'idx' in 'struct hw_perf_event'
     201 |                 hwc->idx = nr;
         |                 ~~~  ^
   drivers/iommu/riscv/iommu-perf.c:224:32: error: no member named 'attr' in 'struct perf_event'
     224 |         unsigned long config = event->attr.config;
         |                                ~~~~~  ^
   drivers/iommu/riscv/iommu-perf.c:252:38: error: no member named 'hw' in 'struct perf_event'
     252 |         struct hw_perf_event *hwc = &event->hw;
         |                                      ~~~~~  ^
   drivers/iommu/riscv/iommu-perf.c:254:13: error: no member named 'attr' in 'struct perf_event'
     254 |         if (event->attr.sample_period)
         |             ~~~~~  ^
>> drivers/iommu/riscv/iommu-perf.c:257:13: error: no member named 'cpu' in 'struct perf_event'
     257 |         if (event->cpu < 0)
         |             ~~~~~  ^
>> drivers/iommu/riscv/iommu-perf.c:260:7: error: no member named 'config' in 'struct hw_perf_event'
     260 |         hwc->config = event->attr.config;
         |         ~~~  ^
   drivers/iommu/riscv/iommu-perf.c:260:23: error: no member named 'attr' in 'struct perf_event'
     260 |         hwc->config = event->attr.config;
         |                       ~~~~~  ^
   drivers/iommu/riscv/iommu-perf.c:276:38: error: no member named 'hw' in 'struct perf_event'
     276 |         struct hw_perf_event *hwc = &event->hw;
         |                                      ~~~~~  ^
>> drivers/iommu/riscv/iommu-perf.c:279:7: error: no member named 'state' in 'struct hw_perf_event'
     279 |         hwc->state = 0;
         |         ~~~  ^
   drivers/iommu/riscv/iommu-perf.c:281:11: error: no member named 'idx' in 'struct hw_perf_event'
     281 |         if (hwc->idx == EVENT_CYCLES)
         |             ~~~  ^
   drivers/iommu/riscv/iommu-perf.c:284:77: error: no member named 'idx' in 'struct hw_perf_event'
     284 |                 count = riscv_iommu_readq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMCTR(hwc->idx));
         |                                                                                      ~~~  ^
>> drivers/iommu/riscv/iommu-perf.c:286:21: error: no member named 'prev_count' in 'struct hw_perf_event'
     286 |         local64_set((&hwc->prev_count), count);
         |                       ~~~  ^
   fatal error: too many errors emitted, stopping now [-ferror-limit=]
   20 errors generated.


vim +157 drivers/iommu/riscv/iommu-perf.c

8cf5fb280f5fc0 Yaxing Guo 2025-09-15  152  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  153  static int riscv_iommu_pmu_event_add(struct riscv_iommu_pmu *iommu_pmu,
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  154  				     struct perf_event *event)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  155  {
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  156  	int nr = -1, of;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15 @157  	unsigned long event_id = event->attr.config;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  158  	riscv_iommu_pmu_cfg1_t config1;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15 @159  	struct hw_perf_event *hwc = &event->hw;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  160  	struct riscv_iommu_device *iommu = iommu_pmu->iommu;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  161  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  162  	config1.val = event->attr.config1;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  163  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  164  	if (event_id >= RISCV_IOMMU_IOHPMCTR_CNT)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  165  		return -EINVAL;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  166  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  167  	if (iommu->hpm_irq)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  168  		of = 0;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  169  	else
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  170  		of = 1;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  171  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  172  	if (event_id == EVENT_CYCLES) {
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  173  		unsigned long val;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  174  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  175  		val = riscv_iommu_readq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMCYCLES);
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  176  		if (of)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  177  			val &= ~RISCV_IOMMU_IOHPMCYCLES_OF;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  178  		else
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  179  			val |= RISCV_IOMMU_IOHPMCYCLES_OF;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  180  		riscv_iommu_writeq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMCYCLES, val);
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  181  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15 @182  		hwc->idx = 0;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  183  		iommu->events[0]->perf_event = event;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  184  	} else {
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  185  		struct riscv_iommu_perf_event *iommu_perf_event;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  186  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  187  		iommu_perf_event = get_riscv_iommu_perf_event(iommu_pmu, event,
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  188  							      config1.pv_pscv, config1.dv_gscv,
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  189  							      config1.idt, config1.pid_pscid,
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  190  							      config1.did_gscid, &nr);
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  191  		if (!iommu_perf_event)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  192  			return -ENOSPC;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  193  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  194  		riscv_iommu_pmu_hpmevt_set(&iommu_pmu->iommu->iohpmevt[nr], event_id,
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  195  					   iommu_perf_event->pv_pscv, iommu_perf_event->dv_gscv,
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  196  					   iommu_perf_event->idt, iommu_perf_event->pid_pscid,
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  197  					   iommu_perf_event->did_gscid, of);
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  198  		riscv_iommu_writeq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMEVT(nr),
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  199  				   iommu_pmu->iommu->iohpmevt[nr].val);
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  200  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  201  		hwc->idx = nr;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  202  	}
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  203  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  204  	return 0;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  205  }
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  206  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  207  static int riscv_iommu_pmu_hpmevt_idx_get(struct riscv_iommu_pmu *iommu_pmu, int event_id)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  208  {
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  209  	int i;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  210  	iohpmevt_t *iohpmevt;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  211  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  212  	for (i = 0; i < RISCV_IOMMU_IOHPMCTR_CNT; i++) {
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  213  		iohpmevt = &iommu_pmu->iommu->iohpmevt[i];
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  214  		if (iohpmevt->eventID == event_id)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  215  			return iohpmevt - iommu_pmu->iommu->iohpmevt;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  216  	}
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  217  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  218  	return -1;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  219  }
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  220  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  221  static int riscv_iommu_event_del(struct riscv_iommu_pmu *iommu_pmu,
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  222  				 struct perf_event *event)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  223  {
8cf5fb280f5fc0 Yaxing Guo 2025-09-15 @224  	unsigned long config = event->attr.config;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  225  	struct riscv_iommu_device *iommu = iommu_pmu->iommu;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  226  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  227  	if (config >= RISCV_IOMMU_IOHPMCTR_CNT)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  228  		return -EINVAL;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  229  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  230  	if (config == EVENT_CYCLES) {
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  231  		iommu->events[0] = NULL;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  232  	} else {
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  233  		int nr;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  234  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  235  		nr = riscv_iommu_pmu_hpmevt_idx_get(iommu_pmu, config);
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  236  		if (-1 == nr)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  237  			return -1;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  238  		riscv_iommu_pmu_hpmevt_set(&iommu_pmu->iommu->iohpmevt[nr], 0,
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  239  					   0, 0, 0, 0, 0, 0);
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  240  		clear_bit(nr, &iommu_pmu->iommu->iohpmctr_bitmap);
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  241  		riscv_iommu_writeq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMEVT(nr),
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  242  				   0);
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  243  		kfree(iommu->events[nr]);
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  244  		iommu->events[nr] = NULL;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  245  	}
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  246  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  247  	return 0;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  248  }
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  249  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  250  static int riscv_iommu_pmu_event_init(struct perf_event *event)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  251  {
8cf5fb280f5fc0 Yaxing Guo 2025-09-15 @252  	struct hw_perf_event *hwc = &event->hw;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  253  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  254  	if (event->attr.sample_period)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  255  		return -EINVAL;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  256  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15 @257  	if (event->cpu < 0)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  258  		return -EINVAL;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  259  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15 @260  	hwc->config = event->attr.config;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  261  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  262  	return 0;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  263  }
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  264  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  265  static void riscv_iommu_pmu_enable(struct pmu *pmu)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  266  {
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  267  }
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  268  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  269  static void riscv_iommu_pmu_disable(struct pmu *pmu)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  270  {
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  271  }
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  272  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  273  static void riscv_iommu_pmu_start(struct perf_event *event, int flags)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  274  {
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  275  	struct riscv_iommu_pmu *iommu_pmu = riscv_iommu_event_to_pmu(event);
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  276  	struct hw_perf_event *hwc = &event->hw;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  277  	unsigned long count;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  278  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15 @279  	hwc->state = 0;
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  280  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  281  	if (hwc->idx == EVENT_CYCLES)
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  282  		count = riscv_iommu_readq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMCYCLES);
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  283  	else
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  284  		count = riscv_iommu_readq(iommu_pmu->iommu, RISCV_IOMMU_REG_IOHPMCTR(hwc->idx));
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  285  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15 @286  	local64_set((&hwc->prev_count), count);
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  287  
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  288  	perf_event_update_userpage(event);
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  289  }
8cf5fb280f5fc0 Yaxing Guo 2025-09-15  290  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Re: [PATCH v1 1/3] iommu/riscv: Add iommu perf driver
  2025-09-15 10:38 ` [PATCH v1 1/3] iommu/riscv: Add iommu perf driver Will Deacon
@ 2025-09-16 10:27   ` guoyaxing
  2025-09-17 12:10     ` Will Deacon
  0 siblings, 1 reply; 10+ messages in thread
From: guoyaxing @ 2025-09-16 10:27 UTC (permalink / raw)
  To: will
  Cc: linux-riscv, iommu, tjeznach, joro, robin.murphy, paul.walmsley,
	palmer, aou, alex, anxu, wangran

Hi, Will


From: Will Deacon



Date: 2025-09-15 18:38



To: Yaxing Guo



CC: linux-riscv; iommu; tjeznach; joro; robin.murphy; paul.walmsley; palmer; aou; alex; anxu; wangran



Subject: Re: [PATCH v1 1/3] iommu/riscv: Add iommu perf driver



On Mon, Sep 15, 2025 at 10:09:09AM +0800, Yaxing Guo wrote:



>> This patch introduces a performance monitor driver for RISC-V IOMMU



>> iohpm implementions that support hardware performance counters.



>> The driver expose RISC-V-IOMMU-specific performance events (eg. cycles,



>> tlb miss ...)through the Linux perf subsystem, enabling developers



>> to profile and optimize I/O translation performance.



>>



>> Key features:



>> - Implements a perf PMU driver for RISC-V IOMMU.



>>



>> - Exposes IOMMU perf events, such as 'cycles', 'tlb_miss'... via



>>   /sys/devices/riscv-iommu-pmu/events/, allowing use with perf



>>   tools:



>>     perf stat -C 0 -e riscv-iommu-pmu/cycles/ ...



>>     perf stat -C 0 -e riscv-iommu-pmu/tlb_miss/ ...



>>     ...



>> - Supports event filtering through configureable attributes exposed



>>   in /sys/devices/riscv-iommu-pmu/format/, including:



>>   dv_gscv, pv_pscv,did_gscid,pid_pscid,idt (riscv iommu spec 5-23).



>>



>> - Implements overflow interrupt handling.



>>



>> - Adds device tree binding support via optional 'pmu-name' property



>>   allowing platform-specific IOMMU implementations to specify a custom



>>   defined event ID list. If not provided, the driver defaults to only



>>   supporting 'cycles'. When set to 'dummy', it enables all 9 standard



>>   event IDs as defined in riscv iommu spec(5-23).



>>



>> Signed-off-by: Yaxing Guo <guoyaxing@bosc.ac.cn>



>> ---



>>  drivers/iommu/riscv/iommu-perf.c | 535 +++++++++++++++++++++++++++++++



>>  drivers/iommu/riscv/iommu-perf.h |  88 +++++



>>  drivers/iommu/riscv/iommu.h      |   8 +



>>  3 files changed, 631 insertions(+)



>>  create mode 100644 drivers/iommu/riscv/iommu-perf.c



>>  create mode 100644 drivers/iommu/riscv/iommu-perf.h



 



> PMU drivers are better placed under drivers/perf/



 



> (the Arm SMMUv3 PMU driver lives there, for example).



I did a quick reading of SMMU pmu driver in drivers/perf/.  However, If the RISC-V IOMMU PMU driver is placed under drivers/perf/ as the SMMU did, it would cause an overlap in the iomem resource region between the two devices(iommu & iommu pmu), because it needs to share interrupt-related registers (such as the IPSR register) with the main IOMMU driver.

Yaxing Guo


> Will
 



_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Re: [PATCH v1 1/3] iommu/riscv: Add iommu perf driver
  2025-09-16 10:27   ` guoyaxing
@ 2025-09-17 12:10     ` Will Deacon
  2025-09-18  3:20       ` 郭亚星
  0 siblings, 1 reply; 10+ messages in thread
From: Will Deacon @ 2025-09-17 12:10 UTC (permalink / raw)
  To: guoyaxing@bosc.ac.cn
  Cc: linux-riscv, iommu, tjeznach, joro, robin.murphy, paul.walmsley,
	palmer, aou, alex, anxu, wangran

[nit: your email client has made a big mess of the thread here]

On Tue, Sep 16, 2025 at 06:27:23PM +0800, guoyaxing@bosc.ac.cn wrote:
> > PMU drivers are better placed under drivers/perf/
> 
> I did a quick reading of SMMU pmu driver in drivers/perf/.  However, If
> the RISC-V IOMMU PMU driver is placed under drivers/perf/ as the SMMU did,
> it would cause an overlap in the iomem resource region between the two
> devices(iommu & iommu pmu), because it needs to share interrupt-related
> registers (such as the IPSR register) with the main IOMMU driver.

Is that not something you can resolve with IRQF_SHARED?

Will

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v1 1/3] iommu/riscv: Add iommu perf driver
  2025-09-17 12:10     ` Will Deacon
@ 2025-09-18  3:20       ` 郭亚星
  2025-09-18 14:20         ` Will Deacon
  0 siblings, 1 reply; 10+ messages in thread
From: 郭亚星 @ 2025-09-18  3:20 UTC (permalink / raw)
  To: Will Deacon
  Cc: linux-riscv, iommu, tjeznach, joro, robin.murphy, paul.walmsley,
	palmer, aou, alex, anxu, wangran

[Sorry, I tried switching a new email client and made some settings...]

On 9/17/2025 8:10 PM, Will Deacon wrote:
> [nit: your email client has made a big mess of the thread here]
> 
> On Tue, Sep 16, 2025 at 06:27:23PM +0800, guoyaxing@bosc.ac.cn wrote:
>>> PMU drivers are better placed under drivers/perf/
>>
>> I did a quick reading of SMMU pmu driver in drivers/perf/.  However, If
>> the RISC-V IOMMU PMU driver is placed under drivers/perf/ as the SMMU did,
>> it would cause an overlap in the iomem resource region between the two
>> devices(iommu & iommu pmu), because it needs to share interrupt-related
>> registers (such as the IPSR register) with the main IOMMU driver.
> 
> Is that not something you can resolve with IRQF_SHARED?
> 

Actually IOMMU perf is using stand-along interrupt, so might not need 
IRQF_SHARED with IOMMU driver.

What I mean is the IOMMU and IOMMU perf share the same registers, such 
as IPSR. This could result in overlap on this iomem regions between 
them, which is highly undesirable.

For now, two ideas come to my mind:
1. Integrate IOMMU perf functionality into IOMMU driver, just as 
previous version patch.

2. Let IOMMU driver export external api for IOMMU perf
(to obtain virtual address of IPSR register) for perf related operations.

So what's your suggestion?

> Will

Yaxing Guo


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v1 1/3] iommu/riscv: Add iommu perf driver
  2025-09-18  3:20       ` 郭亚星
@ 2025-09-18 14:20         ` Will Deacon
  0 siblings, 0 replies; 10+ messages in thread
From: Will Deacon @ 2025-09-18 14:20 UTC (permalink / raw)
  To: 郭亚星
  Cc: linux-riscv, iommu, tjeznach, joro, robin.murphy, paul.walmsley,
	palmer, aou, alex, anxu, wangran

On Thu, Sep 18, 2025 at 11:20:55AM +0800, 郭亚星 wrote:
> [Sorry, I tried switching a new email client and made some settings...]
> 
> On 9/17/2025 8:10 PM, Will Deacon wrote:
> > [nit: your email client has made a big mess of the thread here]
> > 
> > On Tue, Sep 16, 2025 at 06:27:23PM +0800, guoyaxing@bosc.ac.cn wrote:
> > > > PMU drivers are better placed under drivers/perf/
> > > 
> > > I did a quick reading of SMMU pmu driver in drivers/perf/.  However, If
> > > the RISC-V IOMMU PMU driver is placed under drivers/perf/ as the SMMU did,
> > > it would cause an overlap in the iomem resource region between the two
> > > devices(iommu & iommu pmu), because it needs to share interrupt-related
> > > registers (such as the IPSR register) with the main IOMMU driver.
> > 
> > Is that not something you can resolve with IRQF_SHARED?
> > 
> 
> Actually IOMMU perf is using stand-along interrupt, so might not need
> IRQF_SHARED with IOMMU driver.

Hrm. Two separate interrupts that share the same interrupt-handling
registers is an "interesting" design for a piece of hardware :/

> What I mean is the IOMMU and IOMMU perf share the same registers, such as
> IPSR. This could result in overlap on this iomem regions between them, which
> is highly undesirable.
> 
> For now, two ideas come to my mind:
> 1. Integrate IOMMU perf functionality into IOMMU driver, just as previous
> version patch.
> 
> 2. Let IOMMU driver export external api for IOMMU perf
> (to obtain virtual address of IPSR register) for perf related operations.
> 
> So what's your suggestion?

I wonder if you could create the PMU device as a child of the IOMMU?

I'm sure Robin had fun dealing with shared MMIO regions before, but I
can't remember the details (was it the CMN PMU driver?)

Will

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2025-09-18 14:20 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-15  2:09 [PATCH v1 1/3] iommu/riscv: Add iommu perf driver Yaxing Guo
2025-09-15  2:09 ` [PATCH v1 2/3] iommu/riscv: Add Makefile support for RISC-V IOMMU " Yaxing Guo
2025-09-16  3:40   ` kernel test robot
2025-09-15  2:09 ` [PATCH v1 3/3] iommu/riscv: Register RISC-V IOMMU PMU at init time Yaxing Guo
2025-09-15 10:38 ` [PATCH v1 1/3] iommu/riscv: Add iommu perf driver Will Deacon
2025-09-16 10:27   ` guoyaxing
2025-09-17 12:10     ` Will Deacon
2025-09-18  3:20       ` 郭亚星
2025-09-18 14:20         ` Will Deacon
2025-09-16  1:47 ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox