mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v7 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains
@ 2026-11-03 12:02 fangyu.yu
  2026-11-03 12:02 ` [PATCH v7 01/10] iommu/generic_pt: Fix the RISC-V supported feature mask fangyu.yu
                   ` (9 more replies)
  0 siblings, 10 replies; 17+ messages in thread
From: fangyu.yu @ 2026-11-03 12:02 UTC (permalink / raw)
  To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
	baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
	kevin.tian, atish.patra, skhawaja, vasant.hegde, joerg.roedel,
	tanchi25, gong.shuai
  Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv, kvm-riscv

From: Fangyu Yu <fangyu.yu@linux.alibaba.com>

The RISC-V IOMMU architecture defines an AMO_HWAD capability (Hardware
Access/Dirty update) that allows the IOMMU to atomically set the A/D bits
in second-stage PTEs on DMA access.  When DC.tc.GADE is asserted, the IOMMU
autonomously sets D on the first write to a page mapped by an iohgatp
domain.  This series wires that capability up to the iommufd dirty-tracking
interface (IOMMU_HWPT_SET_DIRTY_TRACKING / IOMMU_HWPT_GET_DIRTY_BITMAP) and
reports IOMMU_CAP_DIRTY_TRACKING.

Design notes
============

* The feature is scoped to second-stage (iohgatp) domains only; these are
  the domains created for KVM / VFIO device pass-through when userspace
  allocates an HWPT with IOMMU_HWPT_ALLOC_NEST_PARENT or
  IOMMU_HWPT_ALLOC_DIRTY_TRACKING.  First-stage (iosatp) domains are not
  touched by this series.

* The page-table side plugs into the existing generic_pt dirty hook
  framework (amdv1 / vtdss style).  RISC-V adds the three required PTE
  ops - is_write_dirty / make_write_clean / make_write_dirty.

Testing
=======

* Test on QEMU RISC-V, a nvme and an e1000e device was passed through
  to an L2 guest via vfio-pci + iommufd.

* generic_pt KUnit: the existing test_dirty case now runs and passes for
  the RISC-V 64-bit format.

Follow-up work
==============
* Build a dedicated end-to-end test case that drives the full flow
  (HWPT_ALLOC with DIRTY_TRACKING -> attach -> IOAS_MAP -> generate real
  DMA -> SET_DIRTY_TRACKING -> GET_DIRTY_BITMAP -> verify bitmap against
  expected IOVA footprint) so that the behaviour can be regression-tested
  beyond the KUnit PTE-level coverage.

LLM-based coding assistants were used for code exploration, patch
review, test execution, and drafting/editing messages and this cover
letter. I reviewed and finalized all resulting code and text.

This series functionally depends on [1], which has been posted but is not
yet in mainline.

---
Changes in v7:
    - Rebased on top of v7.4-rc1 (6628e7d4c6b2).
    - Return -EINVAL instead of -ENODEV when the IOMMU does not support
      the iohgatp mode of a second-stage domain, following the attach_dev
      errno semantics and allowing callers such as QEMU to try another
      domain or allocate a new compatible one.
    - Assign dirty_ops only when IOMMU_HWPT_ALLOC_DIRTY_TRACKING is
      requested, and reject attaching a dirty-tracking domain to an IOMMU
      without AMO_HWAD with -EINVAL. (Per Gong Shuai)
    - Link to v6:
      https://lore.kernel.org/linux-iommu/20261031083014.15040-1-fangyu.yu@linux.alibaba.com/
Changes in v6:
    - Drop the RFC tag.
    - Rebased on top of mainline (390febb5805d).
    - Carry the unmerged prerequisite [2] as patch 1.
    - Drop "iommu/riscv: report iommu capabilities"; mainline commit
      3069e8f3e1e9 ("iommu/riscv: Report cache coherency capability")
      already adds riscv_iommu_capable().
    - domain_alloc_paging_flags: drop the redundant flags pre-check and
      supported_flags, and split the per-stage setup into
      riscv_iommu_init_domain_s1() / riscv_iommu_init_domain_s2().
      (Per Jason)
    - Link to v5:
      https://lore.kernel.org/linux-iommu/20261020094106.92722-1-fangyu.yu@linux.alibaba.com/
Changes in v5:
    - Propagate the GADE bit from the new device context into the hardware
      DC in riscv_iommu_iodir_update(), so the pre-enabled GADE actually
      reaches the hardware. (Per Gong Shuai)
    - Issue a global IOTINVAL.GVMA in riscv_iommu_iodir_set_mode(), so that
      second-stage translations cached before a DDTP mode change (e.g. by
      a previous kernel after kexec) are flushed as the specification
      requires. (Per Tan Chi)
    - Rebased on top of v7.3, with [1] and [2] applied.
    - Link to v4:
      https://lore.kernel.org/linux-riscv/20260915032828.11250-1-fangyu.yu@linux.alibaba.com/
Changes in v4 (Jason's suggestions):
    - Rebased the series on top of [1] and [2].
    - Drop the RISC-V-specific pt_num_items_lg2() top-level special case.
    - Validate page-table address widths by translation stage: accept only
      Sv39/Sv48/Sv57 widths for first-stage tables and only Sv39x4/Sv48x4/
      Sv57x4 widths for second-stage tables.
    - Replace the aliased first-stage/second-stage MODE union with
      independent FSC/IOSATP and IOHGATP MODE fields.
    - Add pt_dirty_supported() to restrict RISC-V generic_pt dirty tracking
      to second-stage tables.
    - Link to v3:
      https://lore.kernel.org/linux-riscv/20260821132749.82070-1-fangyu.yu@linux.alibaba.com/
Changes in v3 (Andrew Jones's suggestions):
    - Rebased the series on top of Andrew Jones' generic_pt RISC-V supported
      feature mask fix, which adds PT_FEAT_RISCV_SVPBMT to the supported feature
      set.
    - Added PT_FEAT_RISCV_S2 to the RISC-V generic_pt supported feature mask and
      KUnit feature matrix.
    - Kept PT_FEAT_SIGN_EXTEND only for first-stage RISC-V KUnit configs; second
      stage configs now use PT_FEAT_RISCV_S2 without sign-extension semantics.
    - Split RISC-V IOMMU capability checks into first-stage FSC and second-stage
      IOHGATP helpers.
    - Updated second-stage paging domain setup to use GPA widths 41/50/59 and to
      avoid PT_FEAT_SIGN_EXTEND.
    - Link to v2:
      https://lore.kernel.org/linux-riscv/20260507113706.11400-1-fangyu.yu@linux.alibaba.com/
Changes in v2 (Jason's suggestions):
    - Introduced a single PT_FEAT_RISCV_S2: second-stage selection is driven
      purely by this feature bit.
    - Switched from dynamic DC.tc.GADE toggling to static pre-enable.
    - domain_alloc_paging_flags: follow the switch/case design from other
      drivers.
    - Drop IOMMU_CAP_DEFERRED_FLUSH in riscv_iommu_capable.
    - Remove the .hw_info-related patch.
    - Link to v1:
      https://lore.kernel.org/linux-riscv/20260428131359.34872-1-fangyu.yu@linux.alibaba.com/

[1] https://lore.kernel.org/linux-iommu/1-v2-563ee63886f0+1209-iommupt_armv8_jgg@nvidia.com/
[2] https://lore.kernel.org/linux-iommu/20260818092444.42755-1-andrew.jones@oss.qualcomm.com/

Andrew Jones (1):
  iommu/generic_pt: Fix the RISC-V supported feature mask

Fangyu Yu (6):
  iommupt: Add RISC-V Second-stage (iohgatp) page table support
  iommupt: Add RISC-V dirty tracking PTE ops
  iommu/riscv: Add domain_alloc_paging_flags for second-stage domain
  iommu/riscv: Pre-enable GADE for second-stage domains
  iommu/riscv: Add dirty tracking support for second-stage domains
  iommu/riscv: Add IOTINVAL.GVMA invalidations

Tomasz Jeznach (1):
  RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch

Zong Li (2):
  iommu/riscv: use data structure instead of individual values
  iommu/riscv: support GSCID and GVMA invalidation command

 arch/riscv/kvm/Kconfig                       |   2 +
 drivers/iommu/generic_pt/fmt/iommu_riscv64.c |   3 +-
 drivers/iommu/generic_pt/fmt/riscv.h         | 158 ++++++++++--
 drivers/iommu/riscv/iommu-bits.h             |   7 +
 drivers/iommu/riscv/iommu.c                  | 244 +++++++++++++++----
 include/linux/generic_pt/common.h            |   4 +
 include/linux/generic_pt/iommu.h             |  11 +
 7 files changed, 359 insertions(+), 70 deletions(-)


base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
-- 
2.50.1


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

* [PATCH v7 01/10] iommu/generic_pt: Fix the RISC-V supported feature mask
  2026-11-03 12:02 [PATCH v7 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
@ 2026-11-03 12:02 ` fangyu.yu
  2026-11-03 12:02 ` [PATCH v7 02/10] iommupt: Add RISC-V Second-stage (iohgatp) page table support fangyu.yu
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: fangyu.yu @ 2026-11-03 12:02 UTC (permalink / raw)
  To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
	baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
	kevin.tian, atish.patra, skhawaja, vasant.hegde, joerg.roedel,
	tanchi25, gong.shuai
  Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv, kvm-riscv,
	Andrew Jones, Nutty Liu

From: Andrew Jones <andrew.jones@oss.qualcomm.com>

The RISC-V format advertises Svpbmt to its users, but omits the feature
from PT_SUPPORTED_FEATURES. Add it there so configurations which enable
Svpbmt pass generic page-table feature validation.

Fixes: f196a8668797 ("iommu/riscv: Advertise Svpbmt support to generic page table")
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
Tested-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
---
 drivers/iommu/generic_pt/fmt/iommu_riscv64.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/generic_pt/fmt/iommu_riscv64.c b/drivers/iommu/generic_pt/fmt/iommu_riscv64.c
index b18fc4d109f5..1bb74114fb02 100644
--- a/drivers/iommu/generic_pt/fmt/iommu_riscv64.c
+++ b/drivers/iommu/generic_pt/fmt/iommu_riscv64.c
@@ -7,7 +7,8 @@
 #define PT_SUPPORTED_FEATURES                                  \
 	(BIT(PT_FEAT_SIGN_EXTEND) | BIT(PT_FEAT_FLUSH_RANGE) | \
 	 BIT(PT_FEAT_RISCV_SVNAPOT_64K) |                      \
-	 BIT(PT_FEAT_DETAILED_GATHER))
+	 BIT(PT_FEAT_DETAILED_GATHER) |                        \
+	 BIT(PT_FEAT_RISCV_SVPBMT))
 #define PT_FORCE_ENABLED_FEATURES BIT(PT_FEAT_DETAILED_GATHER)
 
 #include "iommu_template.h"
-- 
2.50.1


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

* [PATCH v7 02/10] iommupt: Add RISC-V Second-stage (iohgatp) page table support
  2026-11-03 12:02 [PATCH v7 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
  2026-11-03 12:02 ` [PATCH v7 01/10] iommu/generic_pt: Fix the RISC-V supported feature mask fangyu.yu
@ 2026-11-03 12:02 ` fangyu.yu
  2026-11-03 12:02 ` [PATCH v7 03/10] iommupt: Add RISC-V dirty tracking PTE ops fangyu.yu
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: fangyu.yu @ 2026-11-03 12:02 UTC (permalink / raw)
  To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
	baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
	kevin.tian, atish.patra, skhawaja, vasant.hegde, joerg.roedel,
	tanchi25, gong.shuai
  Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv, kvm-riscv

From: Fangyu Yu <fangyu.yu@linux.alibaba.com>

Add support for Sv39x4/Sv48x4/Sv57x4 Second-stage page tables used by
the RISC-V IOMMU iohgatp register. The x4 root page table is 16 KiB
instead of the usual 4 KiB, covering 2 extra GPA bits (hw_max_vasz_lg2
= 41/50/59).

Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/generic_pt/fmt/iommu_riscv64.c |   2 +-
 drivers/iommu/generic_pt/fmt/riscv.h         | 109 +++++++++++++++----
 include/linux/generic_pt/common.h            |   4 +
 include/linux/generic_pt/iommu.h             |  11 ++
 4 files changed, 102 insertions(+), 24 deletions(-)

diff --git a/drivers/iommu/generic_pt/fmt/iommu_riscv64.c b/drivers/iommu/generic_pt/fmt/iommu_riscv64.c
index 1bb74114fb02..fa61839e5599 100644
--- a/drivers/iommu/generic_pt/fmt/iommu_riscv64.c
+++ b/drivers/iommu/generic_pt/fmt/iommu_riscv64.c
@@ -8,7 +8,7 @@
 	(BIT(PT_FEAT_SIGN_EXTEND) | BIT(PT_FEAT_FLUSH_RANGE) | \
 	 BIT(PT_FEAT_RISCV_SVNAPOT_64K) |                      \
 	 BIT(PT_FEAT_DETAILED_GATHER) |                        \
-	 BIT(PT_FEAT_RISCV_SVPBMT))
+	 BIT(PT_FEAT_RISCV_SVPBMT) | BIT(PT_FEAT_RISCV_S2))
 #define PT_FORCE_ENABLED_FEATURES BIT(PT_FEAT_DETAILED_GATHER)
 
 #include "iommu_template.h"
diff --git a/drivers/iommu/generic_pt/fmt/riscv.h b/drivers/iommu/generic_pt/fmt/riscv.h
index ae9a76514416..d4445e463fa9 100644
--- a/drivers/iommu/generic_pt/fmt/riscv.h
+++ b/drivers/iommu/generic_pt/fmt/riscv.h
@@ -37,7 +37,16 @@ enum {
 	PT_MAX_OUTPUT_ADDRESS_LG2 = 34,
 	PT_MAX_TOP_LEVEL = 1,
 #else
-	PT_MAX_VA_ADDRESS_LG2 = 57,
+	/*
+	 * PT_MAX_VA_ADDRESS_LG2 is the upper bound accepted by the generic
+	 * pt_iommu_init() range check.  It must cover both first-stage and
+	 * second-stage (G-stage) modes:
+	 *
+	 *   First-stage  (fsc/iosatp): Sv39=39, Sv48=48, Sv57=57
+	 *   Second-stage (iohgatp):    Sv39x4=41, Sv48x4=50, Sv57x4=59
+	 *
+	 */
+	PT_MAX_VA_ADDRESS_LG2 = 59,
 	PT_MAX_OUTPUT_ADDRESS_LG2 = 56,
 	PT_MAX_TOP_LEVEL = 4,
 #endif
@@ -126,6 +135,12 @@ riscvpt_entry_num_contig_lg2(const struct pt_state *pts)
 
 static inline unsigned int riscvpt_num_items_lg2(const struct pt_state *pts)
 {
+	/*
+	 * It is not allowed to call pt_num_items_lg2() at the top level, this
+	 * API restriction is specifically an optimization avoid overheads
+	 * dealing with concatenated tables here.
+	 */
+	PT_WARN_ON(pts->level == pts->range->top_level);
 	return PT_TABLEMEM_LG2SZ - ilog2(sizeof(u64));
 }
 #define pt_num_items_lg2 riscvpt_num_items_lg2
@@ -261,20 +276,41 @@ riscvpt_iommu_fmt_init(struct pt_iommu_riscv_64 *iommu_table,
 		       const struct pt_iommu_riscv_64_cfg *cfg)
 {
 	struct pt_riscv *table = &iommu_table->riscv_64pt;
-
-	switch (cfg->common.hw_max_vasz_lg2) {
-	case 39:
-		pt_top_set_level(&table->common, 2);
-		break;
-	case 48:
-		pt_top_set_level(&table->common, 3);
-		break;
-	case 57:
-		pt_top_set_level(&table->common, 4);
-		break;
-	default:
-		return -EINVAL;
+	unsigned int top_level;
+
+	if (pt_feature(&table->common, PT_FEAT_RISCV_S2)) {
+		/* Second-stage (iohgatp): Sv39x4 / Sv48x4 / Sv57x4. */
+		switch (cfg->common.hw_max_vasz_lg2) {
+		case 41:
+			top_level = 2;
+			break;
+		case 50:
+			top_level = 3;
+			break;
+		case 59:
+			top_level = 4;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else {
+		/* First-stage (fsc/iosatp): Sv39 / Sv48 / Sv57. */
+		switch (cfg->common.hw_max_vasz_lg2) {
+		case 39:
+			top_level = 2;
+			break;
+		case 48:
+			top_level = 3;
+			break;
+		case 57:
+			top_level = 4;
+			break;
+		default:
+			return -EINVAL;
+		}
 	}
+
+	pt_top_set_level(&table->common, top_level);
 	table->common.max_oasz_lg2 =
 		min(PT_MAX_OUTPUT_ADDRESS_LG2, cfg->common.hw_max_oasz_lg2);
 	return 0;
@@ -292,30 +328,57 @@ riscvpt_iommu_fmt_hw_info(struct pt_iommu_riscv_64 *table,
 	PT_WARN_ON(top_phys & ~PT_TOP_PHYS_MASK);
 
 	/*
-	 * See Table 3. Encodings of iosatp.MODE field" for DC.tx.SXL = 0:
-	 *  8 = Sv39 = top level 2
-	 *  9 = Sv38 = top level 3
-	 *  10 = Sv57 = top level 4
+	 * Both first-stage (fsc/iosatp) and second-stage (iohgatp) share the
+	 * same MODE numeric values for a given top level:
+	 *   top_level 2 -> MODE 8  (Sv39 / Sv39x4)
+	 *   top_level 3 -> MODE 9  (Sv48 / Sv48x4)
+	 *   top_level 4 -> MODE 10 (Sv57 / Sv57x4)
+	 *
+	 * See "Table 3. Encodings of iosatp.MODE field" (DC.tc.SXL = 0) and
+	 * "Table 2. Encoding of iohgatp.MODE field" in the RISC-V IOMMU spec.
 	 */
-	info->fsc_iosatp_mode = top_range->top_level + 6;
+	if (pt_feature(&table->riscv_64pt.common, PT_FEAT_RISCV_S2))
+		info->iohgatp_mode = top_range->top_level + 6;
+	else
+		info->fsc_iosatp_mode = top_range->top_level + 6;
 }
 #define pt_iommu_fmt_hw_info riscvpt_iommu_fmt_hw_info
 
 #if defined(GENERIC_PT_KUNIT)
 static const struct pt_iommu_riscv_64_cfg riscv_64_kunit_fmt_cfgs[] = {
-	[0] = { .common.features = BIT(PT_FEAT_RISCV_SVNAPOT_64K),
+	/* First-stage (fsc/iosatp): Sv39 / Sv48 / Sv57 */
+	[0] = { .common.features = BIT(PT_FEAT_SIGN_EXTEND) |
+				      BIT(PT_FEAT_RISCV_SVNAPOT_64K),
 		.common.hw_max_oasz_lg2 = 56,
 		.common.hw_max_vasz_lg2 = 39 },
-	[1] = { .common.features = 0,
+	[1] = { .common.features = BIT(PT_FEAT_SIGN_EXTEND),
 		.common.hw_max_oasz_lg2 = 56,
 		.common.hw_max_vasz_lg2 = 48 },
-	[2] = { .common.features = BIT(PT_FEAT_RISCV_SVNAPOT_64K),
+	[2] = { .common.features = BIT(PT_FEAT_SIGN_EXTEND) |
+				      BIT(PT_FEAT_RISCV_SVNAPOT_64K),
 		.common.hw_max_oasz_lg2 = 56,
 		.common.hw_max_vasz_lg2 = 57 },
+	/*
+	 * Second-stage (iohgatp): Sv39x4 / Sv48x4 / Sv57x4.
+	 */
+	[3] = { .common.features = BIT(PT_FEAT_RISCV_S2) |
+				      BIT(PT_FEAT_RISCV_SVNAPOT_64K),
+		.common.hw_max_oasz_lg2 = 56,
+		.common.hw_max_vasz_lg2 = 41 },
+	[4] = { .common.features = BIT(PT_FEAT_RISCV_S2),
+		.common.hw_max_oasz_lg2 = 56,
+		.common.hw_max_vasz_lg2 = 50 },
+	[5] = { .common.features = BIT(PT_FEAT_RISCV_S2) |
+				      BIT(PT_FEAT_RISCV_SVNAPOT_64K),
+		.common.hw_max_oasz_lg2 = 56,
+		.common.hw_max_vasz_lg2 = 59 },
 };
 #define kunit_fmt_cfgs riscv_64_kunit_fmt_cfgs
+/* Sign extension is only valid for first-stage configs, not S2. */
 enum {
-	KUNIT_FMT_FEATURES = BIT(PT_FEAT_RISCV_SVNAPOT_64K),
+	KUNIT_FMT_FEATURES = BIT(PT_FEAT_SIGN_EXTEND) |
+			     BIT(PT_FEAT_RISCV_SVNAPOT_64K) |
+			     BIT(PT_FEAT_RISCV_S2),
 };
 #endif
 
diff --git a/include/linux/generic_pt/common.h b/include/linux/generic_pt/common.h
index 1b39b27f0cfa..a87d8e3dc26d 100644
--- a/include/linux/generic_pt/common.h
+++ b/include/linux/generic_pt/common.h
@@ -197,6 +197,10 @@ enum {
 	 * Support Svpbmt extension: encode page-based memory type (PBMT) in PTEs.
 	 */
 	PT_FEAT_RISCV_SVPBMT,
+	/*
+	 * Using second-stage / iohgatp address translation.
+	 */
+	PT_FEAT_RISCV_S2,
 
 };
 
diff --git a/include/linux/generic_pt/iommu.h b/include/linux/generic_pt/iommu.h
index 13ebb72a67de..59edb5cf9408 100644
--- a/include/linux/generic_pt/iommu.h
+++ b/include/linux/generic_pt/iommu.h
@@ -328,7 +328,18 @@ struct pt_iommu_riscv_64_cfg {
 
 struct pt_iommu_riscv_64_hw_info {
 	u64 ppn;
+	/*
+	 * First-stage (fsc/iosatp) MODE encoding:
+	 *   8 = Sv39, 9 = Sv48, 10 = Sv57
+	 * Used to program DC.fsc.iosatp.MODE.
+	 */
 	u8 fsc_iosatp_mode;
+	/*
+	 * Second-stage (iohgatp) MODE encoding:
+	 *   8 = Sv39x4, 9 = Sv48x4, 10 = Sv57x4
+	 * Used to program DC.iohgatp.MODE.
+	 */
+	u8 iohgatp_mode;
 };
 
 IOMMU_FORMAT(riscv_64, riscv_64pt);
-- 
2.50.1


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

* [PATCH v7 03/10] iommupt: Add RISC-V dirty tracking PTE ops
  2026-11-03 12:02 [PATCH v7 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
  2026-11-03 12:02 ` [PATCH v7 01/10] iommu/generic_pt: Fix the RISC-V supported feature mask fangyu.yu
  2026-11-03 12:02 ` [PATCH v7 02/10] iommupt: Add RISC-V Second-stage (iohgatp) page table support fangyu.yu
@ 2026-11-03 12:02 ` fangyu.yu
  2026-11-03 12:02 ` [PATCH v7 04/10] iommu/riscv: use data structure instead of individual values fangyu.yu
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: fangyu.yu @ 2026-11-03 12:02 UTC (permalink / raw)
  To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
	baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
	kevin.tian, atish.patra, skhawaja, vasant.hegde, joerg.roedel,
	tanchi25, gong.shuai
  Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv, kvm-riscv

From: Fangyu Yu <fangyu.yu@linux.alibaba.com>

Implement the three dirty-tracking hooks required by the generic page
table framework for the RISC-V format:

  pt_entry_is_write_dirty():
    Check the D bit (bit 7) in the PTE.

  pt_entry_make_write_clean():
    Clear the D bit across the full contiguous range.

  pt_entry_make_write_dirty():
    Atomically set D via try_cmpxchg64() on a single PTE.

Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/generic_pt/fmt/riscv.h | 49 ++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/drivers/iommu/generic_pt/fmt/riscv.h b/drivers/iommu/generic_pt/fmt/riscv.h
index d4445e463fa9..2f4838eb44af 100644
--- a/drivers/iommu/generic_pt/fmt/riscv.h
+++ b/drivers/iommu/generic_pt/fmt/riscv.h
@@ -223,6 +223,55 @@ static inline void riscvpt_attr_from_entry(const struct pt_state *pts,
 }
 #define pt_attr_from_entry riscvpt_attr_from_entry
 
+/*
+ * Dirty tracking: RISC-V PTEs use D (bit 7) as the hardware dirty bit.
+ * When Svnapot 64K is active a leaf entry spans 16 consecutive PTEs; we
+ * must check / clear all of them so that no dirty indication is lost.
+ */
+static inline bool riscvpt_entry_is_write_dirty(const struct pt_state *pts)
+{
+	unsigned int num_contig_lg2 = riscvpt_entry_num_contig_lg2(pts);
+	const pt_riscv_entry_t *tablep =
+		pt_cur_table(pts, pt_riscv_entry_t) +
+		log2_set_mod(pts->index, 0, num_contig_lg2);
+	const pt_riscv_entry_t *end = tablep + log2_to_int(num_contig_lg2);
+
+	for (; tablep != end; tablep++)
+		if (READ_ONCE(*tablep) & RISCVPT_D)
+			return true;
+	return false;
+}
+#define pt_entry_is_write_dirty riscvpt_entry_is_write_dirty
+
+static inline void riscvpt_entry_make_write_clean(struct pt_state *pts)
+{
+	unsigned int num_contig_lg2 = riscvpt_entry_num_contig_lg2(pts);
+	pt_riscv_entry_t *tablep =
+		pt_cur_table(pts, pt_riscv_entry_t) +
+		log2_set_mod(pts->index, 0, num_contig_lg2);
+	pt_riscv_entry_t *end = tablep + log2_to_int(num_contig_lg2);
+
+	for (; tablep != end; tablep++)
+		WRITE_ONCE(*tablep, READ_ONCE(*tablep) & ~(pt_riscv_entry_t)RISCVPT_D);
+}
+#define pt_entry_make_write_clean riscvpt_entry_make_write_clean
+
+static inline bool riscvpt_entry_make_write_dirty(struct pt_state *pts)
+{
+	pt_riscv_entry_t *tablep =
+		pt_cur_table(pts, pt_riscv_entry_t) + pts->index;
+	pt_riscv_entry_t new = pts->entry | RISCVPT_D;
+
+	return try_cmpxchg64(tablep, &pts->entry, new);
+}
+#define pt_entry_make_write_dirty riscvpt_entry_make_write_dirty
+
+static inline bool riscvpt_dirty_supported(struct pt_common *common)
+{
+	return pt_feature(common, PT_FEAT_RISCV_S2);
+}
+#define pt_dirty_supported riscvpt_dirty_supported
+
 /* --- iommu */
 #include <linux/generic_pt/iommu.h>
 #include <linux/iommu.h>
-- 
2.50.1


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

* [PATCH v7 04/10] iommu/riscv: use data structure instead of individual values
  2026-11-03 12:02 [PATCH v7 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
                   ` (2 preceding siblings ...)
  2026-11-03 12:02 ` [PATCH v7 03/10] iommupt: Add RISC-V dirty tracking PTE ops fangyu.yu
@ 2026-11-03 12:02 ` fangyu.yu
  2026-11-03 12:02 ` [PATCH v7 05/10] iommu/riscv: support GSCID and GVMA invalidation command fangyu.yu
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: fangyu.yu @ 2026-11-03 12:02 UTC (permalink / raw)
  To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
	baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
	kevin.tian, atish.patra, skhawaja, vasant.hegde, joerg.roedel,
	tanchi25, gong.shuai
  Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv, kvm-riscv,
	Nutty Liu

From: Zong Li <zong.li@sifive.com>

The parameter will be increased when we need to set up more
bit fields in the device context. Use a data structure to
wrap them up.

Signed-off-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/riscv/iommu.c | 27 +++++++++++++++++----------
 1 file changed, 17 insertions(+), 10 deletions(-)

diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index fbdca69c436e..918fce4aa8c4 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1188,7 +1188,7 @@ static void riscv_iommu_iodir_iotinval(struct riscv_iommu_device *iommu,
  * interim translation faults.
  */
 static void riscv_iommu_iodir_update(struct riscv_iommu_device *iommu,
-				     struct device *dev, u64 fsc, u64 ta)
+				     struct device *dev, struct riscv_iommu_dc *new_dc)
 {
 	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
 	struct riscv_iommu_dc *dc;
@@ -1227,10 +1227,10 @@ static void riscv_iommu_iodir_update(struct riscv_iommu_device *iommu,
 	for (i = 0; i < fwspec->num_ids; i++) {
 		dc = riscv_iommu_get_dc(iommu, fwspec->ids[i]);
 		tc = READ_ONCE(dc->tc);
-		tc |= ta & RISCV_IOMMU_DC_TC_V;
+		tc |= new_dc->ta & RISCV_IOMMU_DC_TC_V;
 
-		WRITE_ONCE(dc->fsc, fsc);
-		WRITE_ONCE(dc->ta, ta & RISCV_IOMMU_PC_TA_PSCID);
+		WRITE_ONCE(dc->fsc, new_dc->fsc);
+		WRITE_ONCE(dc->ta, new_dc->ta & RISCV_IOMMU_PC_TA_PSCID);
 		/* Update device context, write TC.V as the last step. */
 		dma_wmb();
 		WRITE_ONCE(dc->tc, tc);
@@ -1311,22 +1311,22 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
 	struct riscv_iommu_device *iommu = dev_to_iommu(dev);
 	struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
 	struct pt_iommu_riscv_64_hw_info pt_info;
-	u64 fsc, ta;
+	struct riscv_iommu_dc dc = {0};
 
 	pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info);
 
 	if (!riscv_iommu_pt_supported(iommu, pt_info.fsc_iosatp_mode))
 		return -ENODEV;
 
-	fsc = FIELD_PREP(RISCV_IOMMU_PC_FSC_MODE, pt_info.fsc_iosatp_mode) |
+	dc.fsc = FIELD_PREP(RISCV_IOMMU_PC_FSC_MODE, pt_info.fsc_iosatp_mode) |
 	      FIELD_PREP(RISCV_IOMMU_PC_FSC_PPN, pt_info.ppn);
-	ta = FIELD_PREP(RISCV_IOMMU_PC_TA_PSCID, domain->pscid) |
+	dc.ta = FIELD_PREP(RISCV_IOMMU_PC_TA_PSCID, domain->pscid) |
 	     RISCV_IOMMU_PC_TA_V;
 
 	if (riscv_iommu_bond_link(domain, dev))
 		return -ENOMEM;
 
-	riscv_iommu_iodir_update(iommu, dev, fsc, ta);
+	riscv_iommu_iodir_update(iommu, dev, &dc);
 	riscv_iommu_bond_unlink(info->domain, dev);
 	info->domain = domain;
 
@@ -1401,9 +1401,12 @@ static int riscv_iommu_attach_blocking_domain(struct iommu_domain *iommu_domain,
 {
 	struct riscv_iommu_device *iommu = dev_to_iommu(dev);
 	struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
+	struct riscv_iommu_dc dc = {0};
+
+	dc.fsc = RISCV_IOMMU_FSC_BARE;
 
 	/* Make device context invalid, translation requests will fault w/ #258 */
-	riscv_iommu_iodir_update(iommu, dev, RISCV_IOMMU_FSC_BARE, 0);
+	riscv_iommu_iodir_update(iommu, dev, &dc);
 	riscv_iommu_bond_unlink(info->domain, dev);
 	info->domain = NULL;
 
@@ -1423,8 +1426,12 @@ static int riscv_iommu_attach_identity_domain(struct iommu_domain *iommu_domain,
 {
 	struct riscv_iommu_device *iommu = dev_to_iommu(dev);
 	struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
+	struct riscv_iommu_dc dc = {0};
+
+	dc.fsc = RISCV_IOMMU_FSC_BARE;
+	dc.ta = RISCV_IOMMU_PC_TA_V;
 
-	riscv_iommu_iodir_update(iommu, dev, RISCV_IOMMU_FSC_BARE, RISCV_IOMMU_PC_TA_V);
+	riscv_iommu_iodir_update(iommu, dev, &dc);
 	riscv_iommu_bond_unlink(info->domain, dev);
 	info->domain = NULL;
 
-- 
2.50.1


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

* [PATCH v7 05/10] iommu/riscv: support GSCID and GVMA invalidation command
  2026-11-03 12:02 [PATCH v7 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
                   ` (3 preceding siblings ...)
  2026-11-03 12:02 ` [PATCH v7 04/10] iommu/riscv: use data structure instead of individual values fangyu.yu
@ 2026-11-03 12:02 ` fangyu.yu
  2026-11-04  9:21   ` Gong Shuai
  2026-11-03 12:02 ` [PATCH v7 06/10] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch fangyu.yu
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: fangyu.yu @ 2026-11-03 12:02 UTC (permalink / raw)
  To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
	baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
	kevin.tian, atish.patra, skhawaja, vasant.hegde, joerg.roedel,
	tanchi25, gong.shuai
  Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv, kvm-riscv,
	Nutty Liu

From: Zong Li <zong.li@sifive.com>

This patch adds a ID Allocator for GSCID and a wrap for setting up
GSCID in IOTLB invalidation command.

Set up iohgatp to enable second stage table and flush stage-2 table if
the GSCID is set.

The GSCID of domain should be freed when release domain. GSCID will be
allocated for parent domain in nested IOMMU process.

Signed-off-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
---
 drivers/iommu/riscv/iommu-bits.h |  7 ++++
 drivers/iommu/riscv/iommu.c      | 62 ++++++++++++++++++++++++++------
 2 files changed, 58 insertions(+), 11 deletions(-)

diff --git a/drivers/iommu/riscv/iommu-bits.h b/drivers/iommu/riscv/iommu-bits.h
index f2ef9bd3cde9..65d1f2250011 100644
--- a/drivers/iommu/riscv/iommu-bits.h
+++ b/drivers/iommu/riscv/iommu-bits.h
@@ -720,6 +720,13 @@ static inline void riscv_iommu_cmd_inval_vma(struct riscv_iommu_command *cmd)
 	cmd->dword1 = 0;
 }
 
+static inline void riscv_iommu_cmd_inval_gvma(struct riscv_iommu_command *cmd)
+{
+	cmd->dword0 = FIELD_PREP(RISCV_IOMMU_CMD0_OPCODE, RISCV_IOMMU_CMD_IOTINVAL_OPCODE) |
+		      FIELD_PREP(RISCV_IOMMU_CMD0_FUNC, RISCV_IOMMU_CMD_IOTINVAL_FUNC_GVMA);
+	cmd->dword1 = 0;
+}
+
 static inline void riscv_iommu_cmd_inval_set_addr(struct riscv_iommu_command *cmd,
 						  u64 addr)
 {
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 918fce4aa8c4..5546dfcd976c 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -49,6 +49,10 @@
 static DEFINE_IDA(riscv_iommu_pscids);
 #define RISCV_IOMMU_MAX_PSCID		(BIT(20) - 1)
 
+/* IOMMU GSCID allocation namespace. */
+static DEFINE_IDA(riscv_iommu_gscids);
+#define RISCV_IOMMU_MAX_GSCID		(BIT(16) - 1)
+
 /* Device resource-managed allocations */
 struct riscv_iommu_devres {
 	void *addr;
@@ -842,6 +846,7 @@ struct riscv_iommu_domain {
 	struct list_head bonds;
 	spinlock_t lock;		/* protect bonds list updates. */
 	int pscid;
+	int gscid;
 };
 PT_IOMMU_CHECK_DOMAIN(struct riscv_iommu_domain, riscvpt.iommu, domain);
 
@@ -1004,7 +1009,7 @@ static void riscv_iommu_tlbi_calc(struct riscv_iommu_tlbi *tlbi,
 }
 
 static void riscv_iommu_iotlb_inval_iommu(struct riscv_iommu_device *iommu,
-					  int pscid,
+					  int pscid, int gscid,
 					  struct riscv_iommu_tlbi *tlbi)
 {
 	bool use_nl = tlbi->non_leaf &&
@@ -1012,8 +1017,13 @@ static void riscv_iommu_iotlb_inval_iommu(struct riscv_iommu_device *iommu,
 	struct riscv_iommu_command cmd;
 	unsigned int i;
 
-	riscv_iommu_cmd_inval_vma(&cmd);
-	riscv_iommu_cmd_inval_set_pscid(&cmd, pscid);
+	if (gscid) {
+		riscv_iommu_cmd_inval_gvma(&cmd);
+		riscv_iommu_cmd_inval_set_gscid(&cmd, gscid);
+	} else {
+		riscv_iommu_cmd_inval_vma(&cmd);
+		riscv_iommu_cmd_inval_set_pscid(&cmd, pscid);
+	}
 
 	/*
 	 * If non-leaf entries were changed and the IOMMU doesn't
@@ -1097,7 +1107,7 @@ static void riscv_iommu_iotlb_inval(struct riscv_iommu_domain *domain,
 		if (iommu == prev)
 			continue;
 
-		riscv_iommu_iotlb_inval_iommu(iommu, domain->pscid, &tlbi);
+		riscv_iommu_iotlb_inval_iommu(iommu, domain->pscid, domain->gscid, &tlbi);
 		prev = iommu;
 	}
 
@@ -1231,6 +1241,7 @@ static void riscv_iommu_iodir_update(struct riscv_iommu_device *iommu,
 
 		WRITE_ONCE(dc->fsc, new_dc->fsc);
 		WRITE_ONCE(dc->ta, new_dc->ta & RISCV_IOMMU_PC_TA_PSCID);
+		WRITE_ONCE(dc->iohgatp, new_dc->iohgatp);
 		/* Update device context, write TC.V as the last step. */
 		dma_wmb();
 		WRITE_ONCE(dc->tc, tc);
@@ -1281,16 +1292,19 @@ static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
 
 	WARN_ON(!list_empty(&domain->bonds));
 
-	if ((int)domain->pscid > 0)
+	if (domain->pscid > 0)
 		ida_free(&riscv_iommu_pscids, domain->pscid);
+	if (domain->gscid > 0)
+		ida_free(&riscv_iommu_gscids, domain->gscid);
 
 	pt_iommu_deinit(&domain->riscvpt.iommu);
 	kfree(domain);
 }
 
-static bool riscv_iommu_pt_supported(struct riscv_iommu_device *iommu, int pgd_mode)
+static bool riscv_iommu_fsc_supported(struct riscv_iommu_device *iommu,
+				      int mode)
 {
-	switch (pgd_mode) {
+	switch (mode) {
 	case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
 		return iommu->caps & RISCV_IOMMU_CAPABILITIES_SV39;
 
@@ -1303,6 +1317,22 @@ static bool riscv_iommu_pt_supported(struct riscv_iommu_device *iommu, int pgd_m
 	return false;
 }
 
+static bool riscv_iommu_iohgatp_supported(struct riscv_iommu_device *iommu,
+					  int mode)
+{
+	switch (mode) {
+	case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4:
+		return iommu->caps & RISCV_IOMMU_CAPABILITIES_SV39X4;
+
+	case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4:
+		return iommu->caps & RISCV_IOMMU_CAPABILITIES_SV48X4;
+
+	case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4:
+		return iommu->caps & RISCV_IOMMU_CAPABILITIES_SV57X4;
+	}
+	return false;
+}
+
 static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
 					    struct device *dev,
 					    struct iommu_domain *old)
@@ -1315,11 +1345,21 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
 
 	pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info);
 
-	if (!riscv_iommu_pt_supported(iommu, pt_info.fsc_iosatp_mode))
-		return -ENODEV;
+	if (domain->gscid) {
+		if (!riscv_iommu_iohgatp_supported(iommu, pt_info.iohgatp_mode))
+			return -EINVAL;
+
+		dc.iohgatp =
+			FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_MODE, pt_info.iohgatp_mode) |
+			FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_GSCID, domain->gscid) |
+			FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_PPN, pt_info.ppn);
+	} else {
+		if (!riscv_iommu_fsc_supported(iommu, pt_info.fsc_iosatp_mode))
+			return -ENODEV;
 
-	dc.fsc = FIELD_PREP(RISCV_IOMMU_PC_FSC_MODE, pt_info.fsc_iosatp_mode) |
-	      FIELD_PREP(RISCV_IOMMU_PC_FSC_PPN, pt_info.ppn);
+		dc.fsc = FIELD_PREP(RISCV_IOMMU_PC_FSC_MODE, pt_info.fsc_iosatp_mode) |
+			 FIELD_PREP(RISCV_IOMMU_PC_FSC_PPN, pt_info.ppn);
+	}
 	dc.ta = FIELD_PREP(RISCV_IOMMU_PC_TA_PSCID, domain->pscid) |
 	     RISCV_IOMMU_PC_TA_V;
 
-- 
2.50.1


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

* [PATCH v7 06/10] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch
  2026-11-03 12:02 [PATCH v7 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
                   ` (4 preceding siblings ...)
  2026-11-03 12:02 ` [PATCH v7 05/10] iommu/riscv: support GSCID and GVMA invalidation command fangyu.yu
@ 2026-11-03 12:02 ` fangyu.yu
  2026-11-04  6:45   ` Gong Shuai
  2026-11-03 12:02 ` [PATCH v7 07/10] iommu/riscv: Add domain_alloc_paging_flags for second-stage domain fangyu.yu
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: fangyu.yu @ 2026-11-03 12:02 UTC (permalink / raw)
  To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
	baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
	kevin.tian, atish.patra, skhawaja, vasant.hegde, joerg.roedel,
	tanchi25, gong.shuai
  Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv, kvm-riscv,
	Tomasz Jeznach, Andrew Jones

From: Tomasz Jeznach <tjeznach@rivosinc.com>

Enable KVM/VFIO support on RISC-V architecture.

Signed-off-by: Tomasz Jeznach <tjeznach@rivosinc.com>
Signed-off-by: Andrew Jones <ajones@ventanamicro.com>
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
---
 arch/riscv/kvm/Kconfig | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig
index f7e4d0f9b156..13552cfc2cd3 100644
--- a/arch/riscv/kvm/Kconfig
+++ b/arch/riscv/kvm/Kconfig
@@ -28,8 +28,10 @@ config KVM
 	select KVM_GENERIC_HARDWARE_ENABLING
 	select KVM_GENERIC_PRE_FAULT_MEMORY
 	select VIRT_XFER_TO_GUEST_WORK
+	select KVM_VFIO
 	select SCHED_INFO
 	select GUEST_PERF_EVENTS if PERF_EVENTS
+	select SRCU
 	help
 	  Support hosting virtualized guest machines.
 
-- 
2.50.1


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

* [PATCH v7 07/10] iommu/riscv: Add domain_alloc_paging_flags for second-stage domain
  2026-11-03 12:02 [PATCH v7 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
                   ` (5 preceding siblings ...)
  2026-11-03 12:02 ` [PATCH v7 06/10] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch fangyu.yu
@ 2026-11-03 12:02 ` fangyu.yu
  2026-11-03 12:02 ` [PATCH v7 08/10] iommu/riscv: Pre-enable GADE for second-stage domains fangyu.yu
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: fangyu.yu @ 2026-11-03 12:02 UTC (permalink / raw)
  To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
	baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
	kevin.tian, atish.patra, skhawaja, vasant.hegde, joerg.roedel,
	tanchi25, gong.shuai
  Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv, kvm-riscv

From: Fangyu Yu <fangyu.yu@linux.alibaba.com>

Replace .domain_alloc_paging with .domain_alloc_paging_flags so callers
can pass allocation flags to select the appropriate page-table type.

When IOMMU_HWPT_ALLOC_NEST_PARENT or IOMMU_HWPT_ALLOC_DIRTY_TRACKING is
set in @flags, allocate a second-stage (iohgatp) domain.

When @flags is 0 the behaviour is identical to the previous
domain_alloc_paging: first-stage (iosatp) domain.

Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/riscv/iommu.c | 107 ++++++++++++++++++++++++++++--------
 1 file changed, 84 insertions(+), 23 deletions(-)

diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 5546dfcd976c..61dfde11ba13 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1381,25 +1381,74 @@ static const struct iommu_domain_ops riscv_iommu_paging_domain_ops = {
 	.flush_iotlb_all = riscv_iommu_iotlb_flush_all,
 };
 
-static struct iommu_domain *riscv_iommu_alloc_paging_domain(struct device *dev)
+static int riscv_iommu_init_domain_s1(struct device *dev,
+				      struct riscv_iommu_domain *domain,
+				      struct pt_iommu_riscv_64_cfg *cfg)
 {
-	struct pt_iommu_riscv_64_cfg cfg = {};
-	struct riscv_iommu_domain *domain;
-	struct riscv_iommu_device *iommu;
-	int ret;
+	struct riscv_iommu_device *iommu = dev_to_iommu(dev);
 
-	iommu = dev_to_iommu(dev);
+	cfg->common.features |= BIT(PT_FEAT_SIGN_EXTEND);
 	if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV57) {
-		cfg.common.hw_max_vasz_lg2 = 57;
+		cfg->common.hw_max_vasz_lg2 = 57;
 	} else if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV48) {
-		cfg.common.hw_max_vasz_lg2 = 48;
+		cfg->common.hw_max_vasz_lg2 = 48;
 	} else if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV39) {
-		cfg.common.hw_max_vasz_lg2 = 39;
+		cfg->common.hw_max_vasz_lg2 = 39;
 	} else {
 		dev_err(dev, "cannot find supported page table mode\n");
-		return ERR_PTR(-ENODEV);
+		return -ENODEV;
 	}
-	cfg.common.hw_max_oasz_lg2 = 56;
+
+	domain->pscid = ida_alloc_range(&riscv_iommu_pscids, 1,
+					RISCV_IOMMU_MAX_PSCID, GFP_KERNEL);
+	if (domain->pscid < 0)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/*
+ * Second-stage (iohgatp) page table for KVM VFIO device pass-through and dirty
+ * tracking. The GPA space is 2 bits wider than the corresponding first-stage VA
+ * space (x4 root page table), so hw_max_vasz_lg2 values are 41/50/59.
+ */
+static int riscv_iommu_init_domain_s2(struct device *dev,
+				      struct riscv_iommu_domain *domain,
+				      struct pt_iommu_riscv_64_cfg *cfg)
+{
+	struct riscv_iommu_device *iommu = dev_to_iommu(dev);
+
+	cfg->common.features |= BIT(PT_FEAT_RISCV_S2);
+	if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV57X4) {
+		cfg->common.hw_max_vasz_lg2 = 59;
+	} else if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV48X4) {
+		cfg->common.hw_max_vasz_lg2 = 50;
+	} else if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV39X4) {
+		cfg->common.hw_max_vasz_lg2 = 41;
+	} else {
+		dev_err(dev, "cannot find supported page table mode\n");
+		return -ENODEV;
+	}
+
+	domain->gscid = ida_alloc_range(&riscv_iommu_gscids, 1,
+					RISCV_IOMMU_MAX_GSCID, GFP_KERNEL);
+	if (domain->gscid < 0)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static struct iommu_domain *
+riscv_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags,
+				      const struct iommu_user_data *user_data)
+{
+	struct pt_iommu_riscv_64_cfg cfg = {};
+	struct riscv_iommu_domain *domain;
+	struct riscv_iommu_device *iommu;
+	int ret;
+
+	if (user_data)
+		return ERR_PTR(-EOPNOTSUPP);
 
 	domain = kzalloc_obj(*domain);
 	if (!domain)
@@ -1407,12 +1456,13 @@ static struct iommu_domain *riscv_iommu_alloc_paging_domain(struct device *dev)
 
 	INIT_LIST_HEAD_RCU(&domain->bonds);
 	spin_lock_init(&domain->lock);
+	iommu = dev_to_iommu(dev);
+	cfg.common.hw_max_oasz_lg2 = 56;
 	/*
 	 * 6.4 IOMMU capabilities [..] IOMMU implementations must support the
 	 * Svnapot standard extension for NAPOT Translation Contiguity.
 	 */
-	cfg.common.features = BIT(PT_FEAT_SIGN_EXTEND) |
-			      BIT(PT_FEAT_FLUSH_RANGE) |
+	cfg.common.features = BIT(PT_FEAT_FLUSH_RANGE) |
 			      BIT(PT_FEAT_RISCV_SVNAPOT_64K) |
 			      BIT(PT_FEAT_DETAILED_GATHER);
 	if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SVPBMT)
@@ -1420,19 +1470,30 @@ static struct iommu_domain *riscv_iommu_alloc_paging_domain(struct device *dev)
 	domain->riscvpt.iommu.nid = dev_to_node(iommu->dev);
 	domain->domain.ops = &riscv_iommu_paging_domain_ops;
 
-	domain->pscid = ida_alloc_range(&riscv_iommu_pscids, 1,
-					RISCV_IOMMU_MAX_PSCID, GFP_KERNEL);
-	if (domain->pscid < 0) {
-		riscv_iommu_free_paging_domain(&domain->domain);
-		return ERR_PTR(-ENOMEM);
+	switch (flags) {
+	case 0:
+		ret = riscv_iommu_init_domain_s1(dev, domain, &cfg);
+		break;
+	case IOMMU_HWPT_ALLOC_NEST_PARENT:
+	case IOMMU_HWPT_ALLOC_DIRTY_TRACKING:
+	case IOMMU_HWPT_ALLOC_DIRTY_TRACKING | IOMMU_HWPT_ALLOC_NEST_PARENT:
+		ret = riscv_iommu_init_domain_s2(dev, domain, &cfg);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
 	}
+	if (ret)
+		goto err_free;
 
 	ret = pt_iommu_riscv_64_init(&domain->riscvpt, &cfg, GFP_KERNEL);
-	if (ret) {
-		riscv_iommu_free_paging_domain(&domain->domain);
-		return ERR_PTR(ret);
-	}
+	if (ret)
+		goto err_free;
 	return &domain->domain;
+
+err_free:
+	riscv_iommu_free_paging_domain(&domain->domain);
+	return ERR_PTR(ret);
 }
 
 static int riscv_iommu_attach_blocking_domain(struct iommu_domain *iommu_domain,
@@ -1567,7 +1628,7 @@ static const struct iommu_ops riscv_iommu_ops = {
 	.identity_domain = &riscv_iommu_identity_domain,
 	.blocked_domain = &riscv_iommu_blocking_domain,
 	.release_domain = &riscv_iommu_blocking_domain,
-	.domain_alloc_paging = riscv_iommu_alloc_paging_domain,
+	.domain_alloc_paging_flags = riscv_iommu_domain_alloc_paging_flags,
 	.device_group = riscv_iommu_device_group,
 	.probe_device = riscv_iommu_probe_device,
 	.release_device	= riscv_iommu_release_device,
-- 
2.50.1


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

* [PATCH v7 08/10] iommu/riscv: Pre-enable GADE for second-stage domains
  2026-11-03 12:02 [PATCH v7 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
                   ` (6 preceding siblings ...)
  2026-11-03 12:02 ` [PATCH v7 07/10] iommu/riscv: Add domain_alloc_paging_flags for second-stage domain fangyu.yu
@ 2026-11-03 12:02 ` fangyu.yu
  2026-11-03 12:02 ` [PATCH v7 09/10] iommu/riscv: Add dirty tracking support " fangyu.yu
  2026-11-03 12:02 ` [PATCH v7 10/10] iommu/riscv: Add IOTINVAL.GVMA invalidations fangyu.yu
  9 siblings, 0 replies; 17+ messages in thread
From: fangyu.yu @ 2026-11-03 12:02 UTC (permalink / raw)
  To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
	baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
	kevin.tian, atish.patra, skhawaja, vasant.hegde, joerg.roedel,
	tanchi25, gong.shuai
  Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv, kvm-riscv,
	Nutty Liu

From: Fangyu Yu <fangyu.yu@linux.alibaba.com>

Pre-enable RISCV_IOMMU_DC_TC_GADE in the device context when
attaching a second-stage domain, if the IOMMU supports AMO_HWAD.

Software pre-populates second-stage page tables with D set, so
enabling GADE by default does not change normal behavior. When
dirty tracking is enabled, iommufd clears the pre-set D bits and
GADE becomes necessary for hardware to update the dirty bit on
write access.

This avoids toggling GADE dynamically and keeps device context
setup consistent with second-stage domain attachment.

Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
---
 drivers/iommu/riscv/iommu.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 61dfde11ba13..b3f05dfdf278 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1237,6 +1237,8 @@ static void riscv_iommu_iodir_update(struct riscv_iommu_device *iommu,
 	for (i = 0; i < fwspec->num_ids; i++) {
 		dc = riscv_iommu_get_dc(iommu, fwspec->ids[i]);
 		tc = READ_ONCE(dc->tc);
+		tc &= ~RISCV_IOMMU_DC_TC_GADE;
+		tc |= new_dc->tc & RISCV_IOMMU_DC_TC_GADE;
 		tc |= new_dc->ta & RISCV_IOMMU_DC_TC_V;
 
 		WRITE_ONCE(dc->fsc, new_dc->fsc);
@@ -1353,6 +1355,8 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
 			FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_MODE, pt_info.iohgatp_mode) |
 			FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_GSCID, domain->gscid) |
 			FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_PPN, pt_info.ppn);
+		if (iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD)
+			dc.tc |= RISCV_IOMMU_DC_TC_GADE;
 	} else {
 		if (!riscv_iommu_fsc_supported(iommu, pt_info.fsc_iosatp_mode))
 			return -ENODEV;
-- 
2.50.1


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

* [PATCH v7 09/10] iommu/riscv: Add dirty tracking support for second-stage domains
  2026-11-03 12:02 [PATCH v7 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
                   ` (7 preceding siblings ...)
  2026-11-03 12:02 ` [PATCH v7 08/10] iommu/riscv: Pre-enable GADE for second-stage domains fangyu.yu
@ 2026-11-03 12:02 ` fangyu.yu
  2026-11-04  8:45   ` Gong Shuai
  2026-11-03 12:02 ` [PATCH v7 10/10] iommu/riscv: Add IOTINVAL.GVMA invalidations fangyu.yu
  9 siblings, 1 reply; 17+ messages in thread
From: fangyu.yu @ 2026-11-03 12:02 UTC (permalink / raw)
  To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
	baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
	kevin.tian, atish.patra, skhawaja, vasant.hegde, joerg.roedel,
	tanchi25, gong.shuai
  Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv, kvm-riscv,
	Nutty Liu

From: Fangyu Yu <fangyu.yu@linux.alibaba.com>

Add hardware dirty tracking support for second-stage (iohgatp) domains
used in KVM VFIO device pass-through.

The RISC-V IOMMU can automatically set the dirty bit in PTEs on write
access when DC.tc.GADE is set and the hardware has AMO_HWAD capability.
Wire this up to the iommufd dirty tracking interface:

  - riscv_iommu_set_dirty_tracking(): Return success without toggling GADE.

  - riscv_iommu_dirty_ops: Exposes set_dirty_tracking and the generic
    page-table read_and_clear_dirty via IOMMU_PT_DIRTY_OPS(riscv_64).

  - domain_alloc_paging_flags: Assigns dirty_ops to second-stage domains
    when IOMMU_HWPT_ALLOC_DIRTY_TRACKING is requested.

  - riscv_iommu_capable: Reports IOMMU_CAP_DIRTY_TRACKING when
    AMO_HWAD is present.

Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
---
 drivers/iommu/riscv/iommu.c | 32 ++++++++++++++++++++++++++++++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index b3f05dfdf278..ff1043afe973 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1350,6 +1350,10 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
 	if (domain->gscid) {
 		if (!riscv_iommu_iohgatp_supported(iommu, pt_info.iohgatp_mode))
 			return -EINVAL;
+		/* Dirty tracking requires AMO_HWAD on every attached IOMMU */
+		if (iommu_domain->dirty_ops &&
+		    !(iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD))
+			return -EINVAL;
 
 		dc.iohgatp =
 			FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_MODE, pt_info.iohgatp_mode) |
@@ -1377,6 +1381,21 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
 	return 0;
 }
 
+static int riscv_iommu_set_dirty_tracking(struct iommu_domain *iommu_domain,
+					  bool enable)
+{
+	/*
+	 * Always enabled and the dirty bitmap is cleared prior to
+	 * set_dirty_tracking().
+	 */
+	return 0;
+}
+
+static const struct iommu_dirty_ops riscv_iommu_dirty_ops = {
+	IOMMU_PT_DIRTY_OPS(riscv_64),
+	.set_dirty_tracking = riscv_iommu_set_dirty_tracking,
+};
+
 static const struct iommu_domain_ops riscv_iommu_paging_domain_ops = {
 	IOMMU_PT_DOMAIN_OPS(riscv_64),
 	.attach_dev = riscv_iommu_attach_paging_domain,
@@ -1418,7 +1437,8 @@ static int riscv_iommu_init_domain_s1(struct device *dev,
  */
 static int riscv_iommu_init_domain_s2(struct device *dev,
 				      struct riscv_iommu_domain *domain,
-				      struct pt_iommu_riscv_64_cfg *cfg)
+				      struct pt_iommu_riscv_64_cfg *cfg,
+				      u32 flags)
 {
 	struct riscv_iommu_device *iommu = dev_to_iommu(dev);
 
@@ -1439,6 +1459,10 @@ static int riscv_iommu_init_domain_s2(struct device *dev,
 	if (domain->gscid < 0)
 		return -ENOMEM;
 
+	/* AMO_HWAD is checked by iommufd via IOMMU_CAP_DIRTY_TRACKING */
+	if (flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING)
+		domain->domain.dirty_ops = &riscv_iommu_dirty_ops;
+
 	return 0;
 }
 
@@ -1481,7 +1505,7 @@ riscv_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags,
 	case IOMMU_HWPT_ALLOC_NEST_PARENT:
 	case IOMMU_HWPT_ALLOC_DIRTY_TRACKING:
 	case IOMMU_HWPT_ALLOC_DIRTY_TRACKING | IOMMU_HWPT_ALLOC_NEST_PARENT:
-		ret = riscv_iommu_init_domain_s2(dev, domain, &cfg);
+		ret = riscv_iommu_init_domain_s2(dev, domain, &cfg, flags);
 		break;
 	default:
 		ret = -EOPNOTSUPP;
@@ -1559,9 +1583,13 @@ static struct iommu_group *riscv_iommu_device_group(struct device *dev)
 
 static bool riscv_iommu_capable(struct device *dev, enum iommu_cap cap)
 {
+	struct riscv_iommu_device *iommu = dev_to_iommu(dev);
+
 	switch (cap) {
 	case IOMMU_CAP_CACHE_COHERENCY:
 		return dev_is_dma_coherent(dev);
+	case IOMMU_CAP_DIRTY_TRACKING:
+		return !!(iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD);
 	default:
 		return false;
 	}
-- 
2.50.1


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

* [PATCH v7 10/10] iommu/riscv: Add IOTINVAL.GVMA invalidations
  2026-11-03 12:02 [PATCH v7 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
                   ` (8 preceding siblings ...)
  2026-11-03 12:02 ` [PATCH v7 09/10] iommu/riscv: Add dirty tracking support " fangyu.yu
@ 2026-11-03 12:02 ` fangyu.yu
  9 siblings, 0 replies; 17+ messages in thread
From: fangyu.yu @ 2026-11-03 12:02 UTC (permalink / raw)
  To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
	baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
	kevin.tian, atish.patra, skhawaja, vasant.hegde, joerg.roedel,
	tanchi25, gong.shuai
  Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv, kvm-riscv,
	Nutty Liu

From: Fangyu Yu <fangyu.yu@linux.alibaba.com>

Previously, only IOTINVAL.VMA was issued, which is insufficient for
second-stage address translation consistency.

Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
---
 drivers/iommu/riscv/iommu.c | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index ff1043afe973..8c15710d60b6 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -831,6 +831,15 @@ static int riscv_iommu_iodir_set_mode(struct riscv_iommu_device *iommu,
 	riscv_iommu_cmd_inval_vma(&cmd);
 	riscv_iommu_cmd_send(iommu, &cmd);
 
+	/*
+	 * Per the RISC-V IOMMU spec (sec. 5.5), the IOMMU may retain
+	 * cached translations across mode switches. IOTINVAL.VMA only
+	 * covers first-stage leaf PTEs, so use a global IOTINVAL.GVMA
+	 * to flush stale second-stage translations.
+	 */
+	riscv_iommu_cmd_inval_gvma(&cmd);
+	riscv_iommu_cmd_send(iommu, &cmd);
+
 	/* IOFENCE.C */
 	riscv_iommu_cmd_sync(iommu, RISCV_IOMMU_IOTINVAL_TIMEOUT);
 
@@ -1176,12 +1185,15 @@ static void riscv_iommu_iodir_iotinval(struct riscv_iommu_device *iommu,
 		/*
 		 * else: IOTINVAL.VMA with GV=1,AV=PSCV=0,and
 		 * GSCID=DC.iohgatp.GSCID
-		 *
+		 */
+		riscv_iommu_cmd_send(iommu, &cmd);
+		/*
 		 * IOTINVAL.GVMA with GV=1,AV=0,and
 		 * GSCID=DC.iohgatp.GSCID
-		 * TODO: For now, the Second-Stage feature have not yet been merged,
-		 * also issue IOTINVAL.GVMA once second-stage support is merged.
 		 */
+		riscv_iommu_cmd_inval_gvma(&cmd);
+		riscv_iommu_cmd_inval_set_gscid(&cmd,
+			FIELD_GET(RISCV_IOMMU_DC_IOHGATP_GSCID, iohgatp));
 	}
 	riscv_iommu_cmd_send(iommu, &cmd);
 }
-- 
2.50.1


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

* Re: [PATCH v7 06/10] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch
  2026-11-03 12:02 ` [PATCH v7 06/10] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch fangyu.yu
@ 2026-11-04  6:45   ` Gong Shuai
  2026-11-04 10:59     ` fangyu.yu
  0 siblings, 1 reply; 17+ messages in thread
From: Gong Shuai @ 2026-11-04  6:45 UTC (permalink / raw)
  To: fangyu.yu
  Cc: ajones, alex, andrew.jones, anup, aou, atish.patra, baolu.lu,
	gong.shuai, guoren, iommu, jgg, jgg, joerg.roedel, joro, jroedel,
	kevin.tian, kvm-riscv, linux-kernel, linux-riscv, palmer, pjw,
	robin.murphy, skhawaja, tanchi25, tjeznach, tomasz.jeznach,
	vasant.hegde, will, zong.li

Hi Fangyu,

> From: Tomasz Jeznach <tjeznach@rivosinc.com>
> 
> Enable KVM/VFIO support on RISC-V architecture.
> 
> Signed-off-by: Tomasz Jeznach <tjeznach@rivosinc.com>
> Signed-off-by: Andrew Jones <ajones@ventanamicro.com>
> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
> ---
>  arch/riscv/kvm/Kconfig | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig
> index f7e4d0f9b156..13552cfc2cd3 100644
> --- a/arch/riscv/kvm/Kconfig
> +++ b/arch/riscv/kvm/Kconfig
> @@ -28,8 +28,10 @@ config KVM
>  	select KVM_GENERIC_HARDWARE_ENABLING
>  	select KVM_GENERIC_PRE_FAULT_MEMORY
>  	select VIRT_XFER_TO_GUEST_WORK
> +	select KVM_VFIO
>  	select SCHED_INFO
>  	select GUEST_PERF_EVENTS if PERF_EVENTS
> +	select SRCU

This option is now meaningless, please remove it.
See:
https://lore.kernel.org/linux-riscv/20230105003813.1770367-5-paulmck@kernel.org/

Thanks,
Shuai

>  	help
>  	  Support hosting virtualized guest machines.
>  
> -- 
> 2.50.1


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

* Re: [PATCH v7 09/10] iommu/riscv: Add dirty tracking support for second-stage domains
  2026-11-03 12:02 ` [PATCH v7 09/10] iommu/riscv: Add dirty tracking support " fangyu.yu
@ 2026-11-04  8:45   ` Gong Shuai
  2026-11-04 11:50     ` fangyu.yu
  0 siblings, 1 reply; 17+ messages in thread
From: Gong Shuai @ 2026-11-04  8:45 UTC (permalink / raw)
  To: fangyu.yu
  Cc: alex, andrew.jones, anup, aou, atish.patra, baolu.lu, gong.shuai,
	guoren, iommu, jgg, jgg, joerg.roedel, joro, jroedel, kevin.tian,
	kvm-riscv, linux-kernel, linux-riscv, nutty.liu, palmer, pjw,
	robin.murphy, skhawaja, tanchi25, tomasz.jeznach, vasant.hegde,
	will, zong.li

Hi Fangyu,

> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
> 
> Add hardware dirty tracking support for second-stage (iohgatp) domains
> used in KVM VFIO device pass-through.
> 
> The RISC-V IOMMU can automatically set the dirty bit in PTEs on write
> access when DC.tc.GADE is set and the hardware has AMO_HWAD capability.
> Wire this up to the iommufd dirty tracking interface:
> 
>   - riscv_iommu_set_dirty_tracking(): Return success without toggling GADE.
> 
>   - riscv_iommu_dirty_ops: Exposes set_dirty_tracking and the generic
>     page-table read_and_clear_dirty via IOMMU_PT_DIRTY_OPS(riscv_64).
> 
>   - domain_alloc_paging_flags: Assigns dirty_ops to second-stage domains
>     when IOMMU_HWPT_ALLOC_DIRTY_TRACKING is requested.
> 
>   - riscv_iommu_capable: Reports IOMMU_CAP_DIRTY_TRACKING when
>     AMO_HWAD is present.
> 
> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
> ---
>  drivers/iommu/riscv/iommu.c | 32 ++++++++++++++++++++++++++++++--
>  1 file changed, 30 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index b3f05dfdf278..ff1043afe973 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -1350,6 +1350,10 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
>  	if (domain->gscid) {
>  		if (!riscv_iommu_iohgatp_supported(iommu, pt_info.iohgatp_mode))
>  			return -EINVAL;
> +		/* Dirty tracking requires AMO_HWAD on every attached IOMMU */
> +		if (iommu_domain->dirty_ops &&
> +		    !(iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD))
> +			return -EINVAL;
>  
>  		dc.iohgatp =
>  			FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_MODE, pt_info.iohgatp_mode) |
> @@ -1377,6 +1381,21 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
>  	return 0;
>  }
>  
> +static int riscv_iommu_set_dirty_tracking(struct iommu_domain *iommu_domain,
> +					  bool enable)
> +{
> +	/*
> +	 * Always enabled and the dirty bitmap is cleared prior to
> +	 * set_dirty_tracking().
> +	 */
> +	return 0;
> +}
> +
> +static const struct iommu_dirty_ops riscv_iommu_dirty_ops = {
> +	IOMMU_PT_DIRTY_OPS(riscv_64),
> +	.set_dirty_tracking = riscv_iommu_set_dirty_tracking,
> +};
> +
>  static const struct iommu_domain_ops riscv_iommu_paging_domain_ops = {
>  	IOMMU_PT_DOMAIN_OPS(riscv_64),
>  	.attach_dev = riscv_iommu_attach_paging_domain,
> @@ -1418,7 +1437,8 @@ static int riscv_iommu_init_domain_s1(struct device *dev,
>   */
>  static int riscv_iommu_init_domain_s2(struct device *dev,
>  				      struct riscv_iommu_domain *domain,
> -				      struct pt_iommu_riscv_64_cfg *cfg)
> +				      struct pt_iommu_riscv_64_cfg *cfg,
> +				      u32 flags)
>  {
>  	struct riscv_iommu_device *iommu = dev_to_iommu(dev);
>  
> @@ -1439,6 +1459,10 @@ static int riscv_iommu_init_domain_s2(struct device *dev,
>  	if (domain->gscid < 0)
>  		return -ENOMEM;
>  
> +	/* AMO_HWAD is checked by iommufd via IOMMU_CAP_DIRTY_TRACKING */
> +	if (flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING)
> +		domain->domain.dirty_ops = &riscv_iommu_dirty_ops;
> +
>  	return 0;
>  }
>  
> @@ -1481,7 +1505,7 @@ riscv_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags,
>  	case IOMMU_HWPT_ALLOC_NEST_PARENT:
>  	case IOMMU_HWPT_ALLOC_DIRTY_TRACKING:
>  	case IOMMU_HWPT_ALLOC_DIRTY_TRACKING | IOMMU_HWPT_ALLOC_NEST_PARENT:
> -		ret = riscv_iommu_init_domain_s2(dev, domain, &cfg);
> +		ret = riscv_iommu_init_domain_s2(dev, domain, &cfg, flags);
>  		break;
>  	default:
>  		ret = -EOPNOTSUPP;
> @@ -1559,9 +1583,13 @@ static struct iommu_group *riscv_iommu_device_group(struct device *dev)
>  
>  static bool riscv_iommu_capable(struct device *dev, enum iommu_cap cap)
>  {
> +	struct riscv_iommu_device *iommu = dev_to_iommu(dev);
> +
>  	switch (cap) {
>  	case IOMMU_CAP_CACHE_COHERENCY:
>  		return dev_is_dma_coherent(dev);
> +	case IOMMU_CAP_DIRTY_TRACKING:
> +		return !!(iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD);

I'm not quite sure about this. Could there be a device that supports
AMO_HWAD but not any of the S2 modes? GET_HW_INFO would report
DIRTY_TRACKING as supported, but HWPT_ALLOC would then fail in
riscv_iommu_init_domain_s2(). Should this check also include the
S2 modes, like

    AMO_HWAD && (SV39X4|SV48X4|SV57X4) ?

BTW, in riscv_iommu_init_domain_s1/s2, why not return -EOPNOTSUPP
instead of -ENODEV for "cannot find supported page table mode"?

Thanks,
Shuai

>  	default:
>  		return false;
>  	}
> -- 
> 2.50.1
> 

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

* Re: [PATCH v7 05/10] iommu/riscv: support GSCID and GVMA invalidation command
  2026-11-03 12:02 ` [PATCH v7 05/10] iommu/riscv: support GSCID and GVMA invalidation command fangyu.yu
@ 2026-11-04  9:21   ` Gong Shuai
  2026-11-04 11:27     ` fangyu.yu
  0 siblings, 1 reply; 17+ messages in thread
From: Gong Shuai @ 2026-11-04  9:21 UTC (permalink / raw)
  To: fangyu.yu
  Cc: alex, andrew.jones, anup, aou, atish.patra, baolu.lu, gong.shuai,
	guoren, iommu, jgg, jgg, joerg.roedel, joro, jroedel, kevin.tian,
	kvm-riscv, linux-kernel, linux-riscv, nutty.liu, palmer, pjw,
	robin.murphy, skhawaja, tanchi25, tomasz.jeznach, vasant.hegde,
	will, zong.li

Hi Fangyu,

> From: Zong Li <zong.li@sifive.com>
> 
> This patch adds a ID Allocator for GSCID and a wrap for setting up
> GSCID in IOTLB invalidation command.
> 
> Set up iohgatp to enable second stage table and flush stage-2 table if
> the GSCID is set.
> 
> The GSCID of domain should be freed when release domain. GSCID will be
> allocated for parent domain in nested IOMMU process.
> 
> Signed-off-by: Zong Li <zong.li@sifive.com>
> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
> ---
>  drivers/iommu/riscv/iommu-bits.h |  7 ++++
>  drivers/iommu/riscv/iommu.c      | 62 ++++++++++++++++++++++++++------
>  2 files changed, 58 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/iommu/riscv/iommu-bits.h b/drivers/iommu/riscv/iommu-bits.h
> index f2ef9bd3cde9..65d1f2250011 100644
> --- a/drivers/iommu/riscv/iommu-bits.h
> +++ b/drivers/iommu/riscv/iommu-bits.h
> @@ -720,6 +720,13 @@ static inline void riscv_iommu_cmd_inval_vma(struct riscv_iommu_command *cmd)
>  	cmd->dword1 = 0;
>  }
>  
> +static inline void riscv_iommu_cmd_inval_gvma(struct riscv_iommu_command *cmd)
> +{
> +	cmd->dword0 = FIELD_PREP(RISCV_IOMMU_CMD0_OPCODE, RISCV_IOMMU_CMD_IOTINVAL_OPCODE) |
> +		      FIELD_PREP(RISCV_IOMMU_CMD0_FUNC, RISCV_IOMMU_CMD_IOTINVAL_FUNC_GVMA);
> +	cmd->dword1 = 0;
> +}
> +
>  static inline void riscv_iommu_cmd_inval_set_addr(struct riscv_iommu_command *cmd,
>  						  u64 addr)
>  {
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index 918fce4aa8c4..5546dfcd976c 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -49,6 +49,10 @@
>  static DEFINE_IDA(riscv_iommu_pscids);
>  #define RISCV_IOMMU_MAX_PSCID		(BIT(20) - 1)
>  
> +/* IOMMU GSCID allocation namespace. */
> +static DEFINE_IDA(riscv_iommu_gscids);
> +#define RISCV_IOMMU_MAX_GSCID		(BIT(16) - 1)
> +
>  /* Device resource-managed allocations */
>  struct riscv_iommu_devres {
>  	void *addr;
> @@ -842,6 +846,7 @@ struct riscv_iommu_domain {
>  	struct list_head bonds;
>  	spinlock_t lock;		/* protect bonds list updates. */
>  	int pscid;
> +	int gscid;
>  };
>  PT_IOMMU_CHECK_DOMAIN(struct riscv_iommu_domain, riscvpt.iommu, domain);
>  
> @@ -1004,7 +1009,7 @@ static void riscv_iommu_tlbi_calc(struct riscv_iommu_tlbi *tlbi,
>  }
>  
>  static void riscv_iommu_iotlb_inval_iommu(struct riscv_iommu_device *iommu,
> -					  int pscid,
> +					  int pscid, int gscid,
>  					  struct riscv_iommu_tlbi *tlbi)
>  {
>  	bool use_nl = tlbi->non_leaf &&
> @@ -1012,8 +1017,13 @@ static void riscv_iommu_iotlb_inval_iommu(struct riscv_iommu_device *iommu,
>  	struct riscv_iommu_command cmd;
>  	unsigned int i;
>  
> -	riscv_iommu_cmd_inval_vma(&cmd);
> -	riscv_iommu_cmd_inval_set_pscid(&cmd, pscid);
> +	if (gscid) {
> +		riscv_iommu_cmd_inval_gvma(&cmd);
> +		riscv_iommu_cmd_inval_set_gscid(&cmd, gscid);
> +	} else {
> +		riscv_iommu_cmd_inval_vma(&cmd);
> +		riscv_iommu_cmd_inval_set_pscid(&cmd, pscid);
> +	}

As I understand it, the same choice is needed in
riscv_iommu_bond_unlink().

The missing GVMA command does not break anything today, because every
caller of bond_unlink() runs iodir_update() just before it, and that
already sends the GVMA for the old GSCID. But depending on that
ordering seems fragile, and the comment in bond_unlink(), "invalidate
all cached entries for domain's PSCID", is no longer true now that we
have S2 domains.

The change, if you think it is needed, fits better in the last patch,
"iommu/riscv: Add IOTINVAL.GVMA invalidations". I am replying here
only because the code is very similar.

Thanks,
Shuai

>  
>  	/*
>  	 * If non-leaf entries were changed and the IOMMU doesn't
> @@ -1097,7 +1107,7 @@ static void riscv_iommu_iotlb_inval(struct riscv_iommu_domain *domain,
>  		if (iommu == prev)
>  			continue;
>  
> -		riscv_iommu_iotlb_inval_iommu(iommu, domain->pscid, &tlbi);
> +		riscv_iommu_iotlb_inval_iommu(iommu, domain->pscid, domain->gscid, &tlbi);
>  		prev = iommu;
>  	}
>  
> @@ -1231,6 +1241,7 @@ static void riscv_iommu_iodir_update(struct riscv_iommu_device *iommu,
>  
>  		WRITE_ONCE(dc->fsc, new_dc->fsc);
>  		WRITE_ONCE(dc->ta, new_dc->ta & RISCV_IOMMU_PC_TA_PSCID);
> +		WRITE_ONCE(dc->iohgatp, new_dc->iohgatp);
>  		/* Update device context, write TC.V as the last step. */
>  		dma_wmb();
>  		WRITE_ONCE(dc->tc, tc);
> @@ -1281,16 +1292,19 @@ static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
>  
>  	WARN_ON(!list_empty(&domain->bonds));
>  
> -	if ((int)domain->pscid > 0)
> +	if (domain->pscid > 0)
>  		ida_free(&riscv_iommu_pscids, domain->pscid);
> +	if (domain->gscid > 0)
> +		ida_free(&riscv_iommu_gscids, domain->gscid);
>  
>  	pt_iommu_deinit(&domain->riscvpt.iommu);
>  	kfree(domain);
>  }
>  
> -static bool riscv_iommu_pt_supported(struct riscv_iommu_device *iommu, int pgd_mode)
> +static bool riscv_iommu_fsc_supported(struct riscv_iommu_device *iommu,
> +				      int mode)
>  {
> -	switch (pgd_mode) {
> +	switch (mode) {
>  	case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
>  		return iommu->caps & RISCV_IOMMU_CAPABILITIES_SV39;
>  
> @@ -1303,6 +1317,22 @@ static bool riscv_iommu_pt_supported(struct riscv_iommu_device *iommu, int pgd_m
>  	return false;
>  }
>  
> +static bool riscv_iommu_iohgatp_supported(struct riscv_iommu_device *iommu,
> +					  int mode)
> +{
> +	switch (mode) {
> +	case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4:
> +		return iommu->caps & RISCV_IOMMU_CAPABILITIES_SV39X4;
> +
> +	case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4:
> +		return iommu->caps & RISCV_IOMMU_CAPABILITIES_SV48X4;
> +
> +	case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4:
> +		return iommu->caps & RISCV_IOMMU_CAPABILITIES_SV57X4;
> +	}
> +	return false;
> +}
> +
>  static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
>  					    struct device *dev,
>  					    struct iommu_domain *old)
> @@ -1315,11 +1345,21 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
>  
>  	pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info);
>  
> -	if (!riscv_iommu_pt_supported(iommu, pt_info.fsc_iosatp_mode))
> -		return -ENODEV;
> +	if (domain->gscid) {
> +		if (!riscv_iommu_iohgatp_supported(iommu, pt_info.iohgatp_mode))
> +			return -EINVAL;
> +
> +		dc.iohgatp =
> +			FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_MODE, pt_info.iohgatp_mode) |
> +			FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_GSCID, domain->gscid) |
> +			FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_PPN, pt_info.ppn);
> +	} else {
> +		if (!riscv_iommu_fsc_supported(iommu, pt_info.fsc_iosatp_mode))
> +			return -ENODEV;
>  
> -	dc.fsc = FIELD_PREP(RISCV_IOMMU_PC_FSC_MODE, pt_info.fsc_iosatp_mode) |
> -	      FIELD_PREP(RISCV_IOMMU_PC_FSC_PPN, pt_info.ppn);
> +		dc.fsc = FIELD_PREP(RISCV_IOMMU_PC_FSC_MODE, pt_info.fsc_iosatp_mode) |
> +			 FIELD_PREP(RISCV_IOMMU_PC_FSC_PPN, pt_info.ppn);
> +	}
>  	dc.ta = FIELD_PREP(RISCV_IOMMU_PC_TA_PSCID, domain->pscid) |
>  	     RISCV_IOMMU_PC_TA_V;
>  
> -- 
> 2.50.1
> 

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

* Re: [PATCH v7 06/10] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch
  2026-11-04  6:45   ` Gong Shuai
@ 2026-11-04 10:59     ` fangyu.yu
  0 siblings, 0 replies; 17+ messages in thread
From: fangyu.yu @ 2026-11-04 10:59 UTC (permalink / raw)
  To: gong.shuai
  Cc: ajones, alex, andrew.jones, anup, aou, atish.patra, baolu.lu,
	fangyu.yu, guoren, iommu, jgg, jgg, joerg.roedel, joro, jroedel,
	kevin.tian, kvm-riscv, linux-kernel, linux-riscv, palmer, pjw,
	robin.murphy, skhawaja, tanchi25, tjeznach, tomasz.jeznach,
	vasant.hegde, will, zong.li

>Hi Fangyu,
>
>> From: Tomasz Jeznach <tjeznach@rivosinc.com>
>>
>> Enable KVM/VFIO support on RISC-V architecture.
>>
>> Signed-off-by: Tomasz Jeznach <tjeznach@rivosinc.com>
>> Signed-off-by: Andrew Jones <ajones@ventanamicro.com>
>> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>> ---
>>  arch/riscv/kvm/Kconfig | 2 ++
>>  1 file changed, 2 insertions(+)
>>
>> diff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig
>> index f7e4d0f9b156..13552cfc2cd3 100644
>> --- a/arch/riscv/kvm/Kconfig
>> +++ b/arch/riscv/kvm/Kconfig
>> @@ -28,8 +28,10 @@ config KVM
>>  	select KVM_GENERIC_HARDWARE_ENABLING
>>  	select KVM_GENERIC_PRE_FAULT_MEMORY
>>  	select VIRT_XFER_TO_GUEST_WORK
>> +	select KVM_VFIO
>>  	select SCHED_INFO
>>  	select GUEST_PERF_EVENTS if PERF_EVENTS
>> +	select SRCU
>
>This option is now meaningless, please remove it.

Thanks for the heads-up.
I’ll remove it from the next version.

Thanks,
Fangyu

>See:
>https://lore.kernel.org/linux-riscv/20230105003813.1770367-5-paulmck@kernel.org/
>
>Thanks,
>Shuai
>
>>  	help
>>  	  Support hosting virtualized guest machines.
>>
>> --
>> 2.50.1

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

* Re: [PATCH v7 05/10] iommu/riscv: support GSCID and GVMA invalidation command
  2026-11-04  9:21   ` Gong Shuai
@ 2026-11-04 11:27     ` fangyu.yu
  0 siblings, 0 replies; 17+ messages in thread
From: fangyu.yu @ 2026-11-04 11:27 UTC (permalink / raw)
  To: gong.shuai
  Cc: alex, andrew.jones, anup, aou, atish.patra, baolu.lu, fangyu.yu,
	guoren, iommu, jgg, jgg, joerg.roedel, joro, jroedel, kevin.tian,
	kvm-riscv, linux-kernel, linux-riscv, nutty.liu, palmer, pjw,
	robin.murphy, skhawaja, tanchi25, tomasz.jeznach, vasant.hegde,
	will, zong.li

>Hi Fangyu,
>
>> From: Zong Li <zong.li@sifive.com>
>>
>> This patch adds a ID Allocator for GSCID and a wrap for setting up
>> GSCID in IOTLB invalidation command.
>>
>> Set up iohgatp to enable second stage table and flush stage-2 table if
>> the GSCID is set.
>>
>> The GSCID of domain should be freed when release domain. GSCID will be
>> allocated for parent domain in nested IOMMU process.
>>
>> Signed-off-by: Zong Li <zong.li@sifive.com>
>> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
>> ---
>>  drivers/iommu/riscv/iommu-bits.h |  7 ++++
>>  drivers/iommu/riscv/iommu.c      | 62 ++++++++++++++++++++++++++------
>>  2 files changed, 58 insertions(+), 11 deletions(-)
>>
>> diff --git a/drivers/iommu/riscv/iommu-bits.h b/drivers/iommu/riscv/iommu-bits.h
>> index f2ef9bd3cde9..65d1f2250011 100644
>> --- a/drivers/iommu/riscv/iommu-bits.h
>> +++ b/drivers/iommu/riscv/iommu-bits.h
>> @@ -720,6 +720,13 @@ static inline void riscv_iommu_cmd_inval_vma(struct riscv_iommu_command *cmd)
>>  	cmd->dword1 = 0;
>>  }
>>
>> +static inline void riscv_iommu_cmd_inval_gvma(struct riscv_iommu_command *cmd)
>> +{
>> +	cmd->dword0 = FIELD_PREP(RISCV_IOMMU_CMD0_OPCODE, RISCV_IOMMU_CMD_IOTINVAL_OPCODE) |
>> +		      FIELD_PREP(RISCV_IOMMU_CMD0_FUNC, RISCV_IOMMU_CMD_IOTINVAL_FUNC_GVMA);
>> +	cmd->dword1 = 0;
>> +}
>> +
>>  static inline void riscv_iommu_cmd_inval_set_addr(struct riscv_iommu_command *cmd,
>>  						  u64 addr)
>>  {
>> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
>> index 918fce4aa8c4..5546dfcd976c 100644
>> --- a/drivers/iommu/riscv/iommu.c
>> +++ b/drivers/iommu/riscv/iommu.c
>> @@ -49,6 +49,10 @@
>>  static DEFINE_IDA(riscv_iommu_pscids);
>>  #define RISCV_IOMMU_MAX_PSCID		(BIT(20) - 1)
>>
>> +/* IOMMU GSCID allocation namespace. */
>> +static DEFINE_IDA(riscv_iommu_gscids);
>> +#define RISCV_IOMMU_MAX_GSCID		(BIT(16) - 1)
>> +
>>  /* Device resource-managed allocations */
>>  struct riscv_iommu_devres {
>>  	void *addr;
>> @@ -842,6 +846,7 @@ struct riscv_iommu_domain {
>>  	struct list_head bonds;
>>  	spinlock_t lock;		/* protect bonds list updates. */
>>  	int pscid;
>> +	int gscid;
>>  };
>>  PT_IOMMU_CHECK_DOMAIN(struct riscv_iommu_domain, riscvpt.iommu, domain);
>>
>> @@ -1004,7 +1009,7 @@ static void riscv_iommu_tlbi_calc(struct riscv_iommu_tlbi *tlbi,
>>  }
>>
>>  static void riscv_iommu_iotlb_inval_iommu(struct riscv_iommu_device *iommu,
>> -					  int pscid,
>> +					  int pscid, int gscid,
>>  					  struct riscv_iommu_tlbi *tlbi)
>>  {
>>  	bool use_nl = tlbi->non_leaf &&
>> @@ -1012,8 +1017,13 @@ static void riscv_iommu_iotlb_inval_iommu(struct riscv_iommu_device *iommu,
>>  	struct riscv_iommu_command cmd;
>>  	unsigned int i;
>>
>> -	riscv_iommu_cmd_inval_vma(&cmd);
>> -	riscv_iommu_cmd_inval_set_pscid(&cmd, pscid);
>> +	if (gscid) {
>> +		riscv_iommu_cmd_inval_gvma(&cmd);
>> +		riscv_iommu_cmd_inval_set_gscid(&cmd, gscid);
>> +	} else {
>> +		riscv_iommu_cmd_inval_vma(&cmd);
>> +		riscv_iommu_cmd_inval_set_pscid(&cmd, pscid);
>> +	}
>
>As I understand it, the same choice is needed in
>riscv_iommu_bond_unlink().
>
>The missing GVMA command does not break anything today, because every
>caller of bond_unlink() runs iodir_update() just before it, and that
>already sends the GVMA for the old GSCID. But depending on that
>ordering seems fragile, and the comment in bond_unlink(), "invalidate
>all cached entries for domain's PSCID", is no longer true now that we
>have S2 domains.
>

There is no functional issue with the current code, since
iodir_update() already performs the required invalidation.

That said, as this series adds S2 domain support,
bond_unlink() should indeed be updated accordingly.
Good catch.

Thanks,
Fangyu

>The change, if you think it is needed, fits better in the last patch,
>"iommu/riscv: Add IOTINVAL.GVMA invalidations". I am replying here
>only because the code is very similar.
>
>Thanks,
>Shuai
>

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

* Re: [PATCH v7 09/10] iommu/riscv: Add dirty tracking support for second-stage domains
  2026-11-04  8:45   ` Gong Shuai
@ 2026-11-04 11:50     ` fangyu.yu
  0 siblings, 0 replies; 17+ messages in thread
From: fangyu.yu @ 2026-11-04 11:50 UTC (permalink / raw)
  To: gong.shuai
  Cc: alex, andrew.jones, anup, aou, atish.patra, baolu.lu, fangyu.yu,
	guoren, iommu, jgg, jgg, joerg.roedel, joro, jroedel, kevin.tian,
	kvm-riscv, linux-kernel, linux-riscv, nutty.liu, palmer, pjw,
	robin.murphy, skhawaja, tanchi25, tomasz.jeznach, vasant.hegde,
	will, zong.li

>Hi Fangyu,
>
>> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>> 
>> Add hardware dirty tracking support for second-stage (iohgatp) domains
>> used in KVM VFIO device pass-through.
>> 
>> The RISC-V IOMMU can automatically set the dirty bit in PTEs on write
>> access when DC.tc.GADE is set and the hardware has AMO_HWAD capability.
>> Wire this up to the iommufd dirty tracking interface:
>> 
>>   - riscv_iommu_set_dirty_tracking(): Return success without toggling GADE.
>> 
>>   - riscv_iommu_dirty_ops: Exposes set_dirty_tracking and the generic
>>     page-table read_and_clear_dirty via IOMMU_PT_DIRTY_OPS(riscv_64).
>> 
>>   - domain_alloc_paging_flags: Assigns dirty_ops to second-stage domains
>>     when IOMMU_HWPT_ALLOC_DIRTY_TRACKING is requested.
>> 
>>   - riscv_iommu_capable: Reports IOMMU_CAP_DIRTY_TRACKING when
>>     AMO_HWAD is present.
>> 
>> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>> Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
>> ---
>>  drivers/iommu/riscv/iommu.c | 32 ++++++++++++++++++++++++++++++--
>>  1 file changed, 30 insertions(+), 2 deletions(-)
>> 
>> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
>> index b3f05dfdf278..ff1043afe973 100644
>> --- a/drivers/iommu/riscv/iommu.c
>> +++ b/drivers/iommu/riscv/iommu.c
>> @@ -1350,6 +1350,10 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
>>  	if (domain->gscid) {
>>  		if (!riscv_iommu_iohgatp_supported(iommu, pt_info.iohgatp_mode))
>>  			return -EINVAL;
>> +		/* Dirty tracking requires AMO_HWAD on every attached IOMMU */
>> +		if (iommu_domain->dirty_ops &&
>> +		    !(iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD))
>> +			return -EINVAL;
>>  
>>  		dc.iohgatp =
>>  			FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_MODE, pt_info.iohgatp_mode) |
>> @@ -1377,6 +1381,21 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
>>  	return 0;
>>  }
>>  
>> +static int riscv_iommu_set_dirty_tracking(struct iommu_domain *iommu_domain,
>> +					  bool enable)
>> +{
>> +	/*
>> +	 * Always enabled and the dirty bitmap is cleared prior to
>> +	 * set_dirty_tracking().
>> +	 */
>> +	return 0;
>> +}
>> +
>> +static const struct iommu_dirty_ops riscv_iommu_dirty_ops = {
>> +	IOMMU_PT_DIRTY_OPS(riscv_64),
>> +	.set_dirty_tracking = riscv_iommu_set_dirty_tracking,
>> +};
>> +
>>  static const struct iommu_domain_ops riscv_iommu_paging_domain_ops = {
>>  	IOMMU_PT_DOMAIN_OPS(riscv_64),
>>  	.attach_dev = riscv_iommu_attach_paging_domain,
>> @@ -1418,7 +1437,8 @@ static int riscv_iommu_init_domain_s1(struct device *dev,
>>   */
>>  static int riscv_iommu_init_domain_s2(struct device *dev,
>>  				      struct riscv_iommu_domain *domain,
>> -				      struct pt_iommu_riscv_64_cfg *cfg)
>> +				      struct pt_iommu_riscv_64_cfg *cfg,
>> +				      u32 flags)
>>  {
>>  	struct riscv_iommu_device *iommu = dev_to_iommu(dev);
>>  
>> @@ -1439,6 +1459,10 @@ static int riscv_iommu_init_domain_s2(struct device *dev,
>>  	if (domain->gscid < 0)
>>  		return -ENOMEM;
>>  
>> +	/* AMO_HWAD is checked by iommufd via IOMMU_CAP_DIRTY_TRACKING */
>> +	if (flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING)
>> +		domain->domain.dirty_ops = &riscv_iommu_dirty_ops;
>> +
>>  	return 0;
>>  }
>>  
>> @@ -1481,7 +1505,7 @@ riscv_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags,
>>  	case IOMMU_HWPT_ALLOC_NEST_PARENT:
>>  	case IOMMU_HWPT_ALLOC_DIRTY_TRACKING:
>>  	case IOMMU_HWPT_ALLOC_DIRTY_TRACKING | IOMMU_HWPT_ALLOC_NEST_PARENT:
>> -		ret = riscv_iommu_init_domain_s2(dev, domain, &cfg);
>> +		ret = riscv_iommu_init_domain_s2(dev, domain, &cfg, flags);
>>  		break;
>>  	default:
>>  		ret = -EOPNOTSUPP;
>> @@ -1559,9 +1583,13 @@ static struct iommu_group *riscv_iommu_device_group(struct device *dev)
>>  
>>  static bool riscv_iommu_capable(struct device *dev, enum iommu_cap cap)
>>  {
>> +	struct riscv_iommu_device *iommu = dev_to_iommu(dev);
>> +
>>  	switch (cap) {
>>  	case IOMMU_CAP_CACHE_COHERENCY:
>>  		return dev_is_dma_coherent(dev);
>> +	case IOMMU_CAP_DIRTY_TRACKING:
>> +		return !!(iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD);
>
>I'm not quite sure about this. Could there be a device that supports
>AMO_HWAD but not any of the S2 modes? GET_HW_INFO would report
>DIRTY_TRACKING as supported, but HWPT_ALLOC would then fail in
>riscv_iommu_init_domain_s2(). Should this check also include the
>S2 modes, like
>
>    AMO_HWAD && (SV39X4|SV48X4|SV57X4) ?
>

Good question, I had also considered this.

From the IOMMU spec alone, I could not confirm whether
AMO_HWAD implies support for S2 modes.

However, I found this statement in the IOMMU spec:

"The IOMMU must support all the virtual memory
extensions that are supported by any of the harts in
the system."

For virtualization scenarios, we assume the CPU hart
must support at least one S2 mode (hgatp). From this
perspective, the IOMMU should support at least one S2
mode in a virtualization setup, regardless of
AMO_HWAD.

>BTW, in riscv_iommu_init_domain_s1/s2, why not return -EOPNOTSUPP
>instead of -ENODEV for "cannot find supported page table mode"?
>

I used -ENODEV to follow the existing S1 behavior:
no compatible paging mode is available on this IOMMU.

While -EOPNOTSUPP would also be reasonable, I suggest
keeping S1 and S2 consistent and leaving this unchanged
in this series.

Thanks,
Fangyu

>Thanks,
>Shuai
>
>>  	default:
>>  		return false;
>>  	}
>> -- 
>> 2.50.1
>> 

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

end of thread, other threads:[~2026-11-04 11:50 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-03 12:02 [PATCH v7 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
2026-11-03 12:02 ` [PATCH v7 01/10] iommu/generic_pt: Fix the RISC-V supported feature mask fangyu.yu
2026-11-03 12:02 ` [PATCH v7 02/10] iommupt: Add RISC-V Second-stage (iohgatp) page table support fangyu.yu
2026-11-03 12:02 ` [PATCH v7 03/10] iommupt: Add RISC-V dirty tracking PTE ops fangyu.yu
2026-11-03 12:02 ` [PATCH v7 04/10] iommu/riscv: use data structure instead of individual values fangyu.yu
2026-11-03 12:02 ` [PATCH v7 05/10] iommu/riscv: support GSCID and GVMA invalidation command fangyu.yu
2026-11-04  9:21   ` Gong Shuai
2026-11-04 11:27     ` fangyu.yu
2026-11-03 12:02 ` [PATCH v7 06/10] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch fangyu.yu
2026-11-04  6:45   ` Gong Shuai
2026-11-04 10:59     ` fangyu.yu
2026-11-03 12:02 ` [PATCH v7 07/10] iommu/riscv: Add domain_alloc_paging_flags for second-stage domain fangyu.yu
2026-11-03 12:02 ` [PATCH v7 08/10] iommu/riscv: Pre-enable GADE for second-stage domains fangyu.yu
2026-11-03 12:02 ` [PATCH v7 09/10] iommu/riscv: Add dirty tracking support " fangyu.yu
2026-11-04  8:45   ` Gong Shuai
2026-11-04 11:50     ` fangyu.yu
2026-11-03 12:02 ` [PATCH v7 10/10] iommu/riscv: Add IOTINVAL.GVMA invalidations fangyu.yu

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

all inboxes | Powered by JetHome®