* [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-15 6:48 ` Zhanpeng Zhang
0 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-06-15 6:48 UTC (permalink / raw)
To: Tomasz Jeznach, Joerg Roedel, Will Deacon
Cc: Robin Murphy, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, iommu, linux-riscv, linux-kernel, Xu Lu,
cuiyunhui, yuanzhu, Zhanpeng Zhang
Some RISC-V IOMMU implementations cannot perform 64-bit MMIO accesses
to the IOMMU register file. The RISC-V IOMMU architecture allows 64-bit
registers to be accessed using 32-bit accesses, provided the accesses are
properly aligned and do not span multiple registers.
Add a config option for such implementations and access 64-bit IOMMU
registers as paired 32-bit MMIO operations when it is enabled. Serialize
the paired accesses so the high and low halves cannot interleave with
another CPU. Full 64-bit register programming writes the high half before
the low half.
This option describes the register access width. It is not an RV32 kernel
mode and does not describe a 32-bit IOMMU architecture.
Co-developed-by: Xu Lu <luxu.kernel@bytedance.com>
Signed-off-by: Xu Lu <luxu.kernel@bytedance.com>
Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
---
This is needed for platforms whose RISC-V IOMMU register window does not
support naturally aligned 64-bit MMIO accesses.
drivers/iommu/riscv/Kconfig | 11 ++++++++
drivers/iommu/riscv/iommu.c | 4 +++
drivers/iommu/riscv/iommu.h | 55 +++++++++++++++++++++++++++++++++----
3 files changed, 64 insertions(+), 6 deletions(-)
diff --git a/drivers/iommu/riscv/Kconfig b/drivers/iommu/riscv/Kconfig
index b86e5ab94183..54d624b9b2ef 100644
--- a/drivers/iommu/riscv/Kconfig
+++ b/drivers/iommu/riscv/Kconfig
@@ -22,3 +22,14 @@ config RISCV_IOMMU_PCI
def_bool y if RISCV_IOMMU && PCI_MSI
help
Support for the PCIe implementation of RISC-V IOMMU architecture.
+
+config RISCV_IOMMU_32BIT_ACCESS
+ bool "Use 32-bit accesses for RISC-V IOMMU registers"
+ depends on RISCV_IOMMU
+ help
+ Say Y when the RISC-V IOMMU MMIO window cannot be accessed
+ using naturally aligned 64-bit loads and stores.
+
+ When enabled, 64-bit IOMMU registers are accessed as paired
+ 32-bit MMIO operations. This option does not describe an RV32
+ kernel or a 32-bit IOMMU architecture.
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index a31f50bbad35..7fa1721b5728 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -53,6 +53,10 @@ struct riscv_iommu_devres {
void *addr;
};
+#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
+DEFINE_RAW_SPINLOCK(riscv_iommu_32bit_access_lock);
+#endif
+
static void riscv_iommu_devres_pages_release(struct device *dev, void *res)
{
struct riscv_iommu_devres *devres = res;
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 46df79dd5495..ba78ef1858c5 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -14,6 +14,9 @@
#include <linux/iommu.h>
#include <linux/types.h>
#include <linux/iopoll.h>
+#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
+#include <linux/spinlock.h>
+#endif
#include "iommu-bits.h"
@@ -69,21 +72,61 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
#define riscv_iommu_readl(iommu, addr) \
readl_relaxed((iommu)->reg + (addr))
-#define riscv_iommu_readq(iommu, addr) \
- readq_relaxed((iommu)->reg + (addr))
-
#define riscv_iommu_writel(iommu, addr, val) \
writel_relaxed((val), (iommu)->reg + (addr))
+#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
+ readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
+ delay_us, timeout_us)
+
+#ifndef CONFIG_RISCV_IOMMU_32BIT_ACCESS
+#define riscv_iommu_readq(iommu, addr) \
+ readq_relaxed((iommu)->reg + (addr))
+
#define riscv_iommu_writeq(iommu, addr, val) \
writeq_relaxed((val), (iommu)->reg + (addr))
#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
delay_us, timeout_us)
+#else /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
-#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
- readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
- delay_us, timeout_us)
+extern raw_spinlock_t riscv_iommu_32bit_access_lock;
+
+static inline u64 __riscv_iommu_readq_relaxed(void __iomem *addr)
+{
+ u32 lo, hi;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
+ do {
+ hi = readl_relaxed(addr + sizeof(u32));
+ lo = readl_relaxed(addr);
+ } while (hi != readl_relaxed(addr + sizeof(u32)));
+ raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
+
+ return ((u64)hi << 32) | (u64)lo;
+}
+
+static inline void __riscv_iommu_writeq_relaxed(u64 value, void __iomem *addr)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
+ writel_relaxed((u32)(value >> 32), addr + sizeof(u32));
+ writel_relaxed((u32)value, addr);
+ raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
+}
+
+#define riscv_iommu_readq(iommu, addr) \
+ __riscv_iommu_readq_relaxed((iommu)->reg + (addr))
+
+#define riscv_iommu_writeq(iommu, addr, val) \
+ __riscv_iommu_writeq_relaxed((val), (iommu)->reg + (addr))
+
+#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
+ readx_poll_timeout(__riscv_iommu_readq_relaxed, (iommu)->reg + (addr), \
+ val, cond, delay_us, timeout_us)
+#endif /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
#endif
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-15 6:48 ` Zhanpeng Zhang
0 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-06-15 6:48 UTC (permalink / raw)
To: Tomasz Jeznach, Joerg Roedel, Will Deacon
Cc: Robin Murphy, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, iommu, linux-riscv, linux-kernel, Xu Lu,
cuiyunhui, yuanzhu, Zhanpeng Zhang
Some RISC-V IOMMU implementations cannot perform 64-bit MMIO accesses
to the IOMMU register file. The RISC-V IOMMU architecture allows 64-bit
registers to be accessed using 32-bit accesses, provided the accesses are
properly aligned and do not span multiple registers.
Add a config option for such implementations and access 64-bit IOMMU
registers as paired 32-bit MMIO operations when it is enabled. Serialize
the paired accesses so the high and low halves cannot interleave with
another CPU. Full 64-bit register programming writes the high half before
the low half.
This option describes the register access width. It is not an RV32 kernel
mode and does not describe a 32-bit IOMMU architecture.
Co-developed-by: Xu Lu <luxu.kernel@bytedance.com>
Signed-off-by: Xu Lu <luxu.kernel@bytedance.com>
Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
---
This is needed for platforms whose RISC-V IOMMU register window does not
support naturally aligned 64-bit MMIO accesses.
drivers/iommu/riscv/Kconfig | 11 ++++++++
drivers/iommu/riscv/iommu.c | 4 +++
drivers/iommu/riscv/iommu.h | 55 +++++++++++++++++++++++++++++++++----
3 files changed, 64 insertions(+), 6 deletions(-)
diff --git a/drivers/iommu/riscv/Kconfig b/drivers/iommu/riscv/Kconfig
index b86e5ab94183..54d624b9b2ef 100644
--- a/drivers/iommu/riscv/Kconfig
+++ b/drivers/iommu/riscv/Kconfig
@@ -22,3 +22,14 @@ config RISCV_IOMMU_PCI
def_bool y if RISCV_IOMMU && PCI_MSI
help
Support for the PCIe implementation of RISC-V IOMMU architecture.
+
+config RISCV_IOMMU_32BIT_ACCESS
+ bool "Use 32-bit accesses for RISC-V IOMMU registers"
+ depends on RISCV_IOMMU
+ help
+ Say Y when the RISC-V IOMMU MMIO window cannot be accessed
+ using naturally aligned 64-bit loads and stores.
+
+ When enabled, 64-bit IOMMU registers are accessed as paired
+ 32-bit MMIO operations. This option does not describe an RV32
+ kernel or a 32-bit IOMMU architecture.
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index a31f50bbad35..7fa1721b5728 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -53,6 +53,10 @@ struct riscv_iommu_devres {
void *addr;
};
+#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
+DEFINE_RAW_SPINLOCK(riscv_iommu_32bit_access_lock);
+#endif
+
static void riscv_iommu_devres_pages_release(struct device *dev, void *res)
{
struct riscv_iommu_devres *devres = res;
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 46df79dd5495..ba78ef1858c5 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -14,6 +14,9 @@
#include <linux/iommu.h>
#include <linux/types.h>
#include <linux/iopoll.h>
+#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
+#include <linux/spinlock.h>
+#endif
#include "iommu-bits.h"
@@ -69,21 +72,61 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
#define riscv_iommu_readl(iommu, addr) \
readl_relaxed((iommu)->reg + (addr))
-#define riscv_iommu_readq(iommu, addr) \
- readq_relaxed((iommu)->reg + (addr))
-
#define riscv_iommu_writel(iommu, addr, val) \
writel_relaxed((val), (iommu)->reg + (addr))
+#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
+ readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
+ delay_us, timeout_us)
+
+#ifndef CONFIG_RISCV_IOMMU_32BIT_ACCESS
+#define riscv_iommu_readq(iommu, addr) \
+ readq_relaxed((iommu)->reg + (addr))
+
#define riscv_iommu_writeq(iommu, addr, val) \
writeq_relaxed((val), (iommu)->reg + (addr))
#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
delay_us, timeout_us)
+#else /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
-#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
- readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
- delay_us, timeout_us)
+extern raw_spinlock_t riscv_iommu_32bit_access_lock;
+
+static inline u64 __riscv_iommu_readq_relaxed(void __iomem *addr)
+{
+ u32 lo, hi;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
+ do {
+ hi = readl_relaxed(addr + sizeof(u32));
+ lo = readl_relaxed(addr);
+ } while (hi != readl_relaxed(addr + sizeof(u32)));
+ raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
+
+ return ((u64)hi << 32) | (u64)lo;
+}
+
+static inline void __riscv_iommu_writeq_relaxed(u64 value, void __iomem *addr)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
+ writel_relaxed((u32)(value >> 32), addr + sizeof(u32));
+ writel_relaxed((u32)value, addr);
+ raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
+}
+
+#define riscv_iommu_readq(iommu, addr) \
+ __riscv_iommu_readq_relaxed((iommu)->reg + (addr))
+
+#define riscv_iommu_writeq(iommu, addr, val) \
+ __riscv_iommu_writeq_relaxed((val), (iommu)->reg + (addr))
+
+#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
+ readx_poll_timeout(__riscv_iommu_readq_relaxed, (iommu)->reg + (addr), \
+ val, cond, delay_us, timeout_us)
+#endif /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
#endif
--
2.50.1 (Apple Git-155)
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-15 6:48 ` Zhanpeng Zhang
@ 2026-06-15 8:21 ` Andreas Schwab
-1 siblings, 0 replies; 70+ messages in thread
From: Andreas Schwab @ 2026-06-15 8:21 UTC (permalink / raw)
To: Zhanpeng Zhang
Cc: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, iommu,
linux-riscv, linux-kernel, Xu Lu, cuiyunhui, yuanzhu
On Jun 15 2026, Zhanpeng Zhang wrote:
> +config RISCV_IOMMU_32BIT_ACCESS
> + bool "Use 32-bit accesses for RISC-V IOMMU registers"
> + depends on RISCV_IOMMU
> + help
> + Say Y when the RISC-V IOMMU MMIO window cannot be accessed
> + using naturally aligned 64-bit loads and stores.
> +
> + When enabled, 64-bit IOMMU registers are accessed as paired
> + 32-bit MMIO operations. This option does not describe an RV32
> + kernel or a 32-bit IOMMU architecture.
What is the expected setting in a generic kernel?
--
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-15 8:21 ` Andreas Schwab
0 siblings, 0 replies; 70+ messages in thread
From: Andreas Schwab @ 2026-06-15 8:21 UTC (permalink / raw)
To: Zhanpeng Zhang
Cc: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, iommu,
linux-riscv, linux-kernel, Xu Lu, cuiyunhui, yuanzhu
On Jun 15 2026, Zhanpeng Zhang wrote:
> +config RISCV_IOMMU_32BIT_ACCESS
> + bool "Use 32-bit accesses for RISC-V IOMMU registers"
> + depends on RISCV_IOMMU
> + help
> + Say Y when the RISC-V IOMMU MMIO window cannot be accessed
> + using naturally aligned 64-bit loads and stores.
> +
> + When enabled, 64-bit IOMMU registers are accessed as paired
> + 32-bit MMIO operations. This option does not describe an RV32
> + kernel or a 32-bit IOMMU architecture.
What is the expected setting in a generic kernel?
--
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [External] Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-15 8:21 ` Andreas Schwab
@ 2026-06-15 9:51 ` Zhanpeng Zhang
-1 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-06-15 9:51 UTC (permalink / raw)
To: Andreas Schwab
Cc: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, iommu,
linux-riscv, linux-kernel, Xu Lu, cuiyunhui, yuanzhu
Hi Andreas,
For a generic kernel, the expected setting is to leave
RISCV_IOMMU_32BIT_ACCESS disabled.
If supporting such platforms from a single generic kernel is preferred, I can
look into replacing this build-time option with a runtime indication.
Regards,
Zhanpeng
> From: "Andreas Schwab"<schwab@suse.de>
> Date: Mon, Jun 15, 2026, 16:22
> Subject: [External] Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
> To: "Zhanpeng Zhang"<zhangzhanpeng.jasper@bytedance.com>
> Cc: "Tomasz Jeznach"<tjeznach@rivosinc.com>, "Joerg Roedel"<joro@8bytes.org>, "Will Deacon"<will@kernel.org>, "Robin Murphy"<robin.murphy@arm.com>, "Paul Walmsley"<pjw@kernel.org>, "Palmer Dabbelt"<palmer@dabbelt.com>, "Albert Ou"<aou@eecs.berkeley.edu>, "Alexandre Ghiti"<alex@ghiti.fr>, <iommu@lists.linux.dev>, <linux-riscv@lists.infradead.org>, <linux-kernel@vger.kernel.org>, "Xu Lu"<luxu.kernel@bytedance.com>, <cuiyunhui@bytedance.com>, <yuanzhu@bytedance.com>
> On Jun 15 2026, Zhanpeng Zhang wrote:
>
> > +config RISCV_IOMMU_32BIT_ACCESS
> > + bool "Use 32-bit accesses for RISC-V IOMMU registers"
> > + depends on RISCV_IOMMU
> > + help
> > + Say Y when the RISC-V IOMMU MMIO window cannot be accessed
> > + using naturally aligned 64-bit loads and stores.
> > +
> > + When enabled, 64-bit IOMMU registers are accessed as paired
> > + 32-bit MMIO operations. This option does not describe an RV32
> > + kernel or a 32-bit IOMMU architecture.
>
> What is the expected setting in a generic kernel?
>
> --
> Andreas Schwab, SUSE Labs, schwab@suse.de
> GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7
> "And now for something completely different."
>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [External] Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-15 9:51 ` Zhanpeng Zhang
0 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-06-15 9:51 UTC (permalink / raw)
To: Andreas Schwab
Cc: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, iommu,
linux-riscv, linux-kernel, Xu Lu, cuiyunhui, yuanzhu
Hi Andreas,
For a generic kernel, the expected setting is to leave
RISCV_IOMMU_32BIT_ACCESS disabled.
If supporting such platforms from a single generic kernel is preferred, I can
look into replacing this build-time option with a runtime indication.
Regards,
Zhanpeng
> From: "Andreas Schwab"<schwab@suse.de>
> Date: Mon, Jun 15, 2026, 16:22
> Subject: [External] Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
> To: "Zhanpeng Zhang"<zhangzhanpeng.jasper@bytedance.com>
> Cc: "Tomasz Jeznach"<tjeznach@rivosinc.com>, "Joerg Roedel"<joro@8bytes.org>, "Will Deacon"<will@kernel.org>, "Robin Murphy"<robin.murphy@arm.com>, "Paul Walmsley"<pjw@kernel.org>, "Palmer Dabbelt"<palmer@dabbelt.com>, "Albert Ou"<aou@eecs.berkeley.edu>, "Alexandre Ghiti"<alex@ghiti.fr>, <iommu@lists.linux.dev>, <linux-riscv@lists.infradead.org>, <linux-kernel@vger.kernel.org>, "Xu Lu"<luxu.kernel@bytedance.com>, <cuiyunhui@bytedance.com>, <yuanzhu@bytedance.com>
> On Jun 15 2026, Zhanpeng Zhang wrote:
>
> > +config RISCV_IOMMU_32BIT_ACCESS
> > + bool "Use 32-bit accesses for RISC-V IOMMU registers"
> > + depends on RISCV_IOMMU
> > + help
> > + Say Y when the RISC-V IOMMU MMIO window cannot be accessed
> > + using naturally aligned 64-bit loads and stores.
> > +
> > + When enabled, 64-bit IOMMU registers are accessed as paired
> > + 32-bit MMIO operations. This option does not describe an RV32
> > + kernel or a 32-bit IOMMU architecture.
>
> What is the expected setting in a generic kernel?
>
> --
> Andreas Schwab, SUSE Labs, schwab@suse.de
> GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7
> "And now for something completely different."
>
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-15 6:48 ` Zhanpeng Zhang
@ 2026-06-15 9:59 ` David Laight
-1 siblings, 0 replies; 70+ messages in thread
From: David Laight @ 2026-06-15 9:59 UTC (permalink / raw)
To: Zhanpeng Zhang
Cc: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, iommu,
linux-riscv, linux-kernel, Xu Lu, cuiyunhui, yuanzhu
On Mon, 15 Jun 2026 14:48:55 +0800
Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com> wrote:
> Some RISC-V IOMMU implementations cannot perform 64-bit MMIO accesses
> to the IOMMU register file. The RISC-V IOMMU architecture allows 64-bit
> registers to be accessed using 32-bit accesses, provided the accesses are
> properly aligned and do not span multiple registers.
>
> Add a config option for such implementations and access 64-bit IOMMU
> registers as paired 32-bit MMIO operations when it is enabled. Serialize
> the paired accesses so the high and low halves cannot interleave with
> another CPU. Full 64-bit register programming writes the high half before
> the low half.
>
> This option describes the register access width. It is not an RV32 kernel
> mode and does not describe a 32-bit IOMMU architecture.
>
> Co-developed-by: Xu Lu <luxu.kernel@bytedance.com>
> Signed-off-by: Xu Lu <luxu.kernel@bytedance.com>
> Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> ---
> This is needed for platforms whose RISC-V IOMMU register window does not
> support naturally aligned 64-bit MMIO accesses.
>
> drivers/iommu/riscv/Kconfig | 11 ++++++++
> drivers/iommu/riscv/iommu.c | 4 +++
> drivers/iommu/riscv/iommu.h | 55 +++++++++++++++++++++++++++++++++----
> 3 files changed, 64 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/iommu/riscv/Kconfig b/drivers/iommu/riscv/Kconfig
> index b86e5ab94183..54d624b9b2ef 100644
> --- a/drivers/iommu/riscv/Kconfig
> +++ b/drivers/iommu/riscv/Kconfig
> @@ -22,3 +22,14 @@ config RISCV_IOMMU_PCI
> def_bool y if RISCV_IOMMU && PCI_MSI
> help
> Support for the PCIe implementation of RISC-V IOMMU architecture.
> +
> +config RISCV_IOMMU_32BIT_ACCESS
> + bool "Use 32-bit accesses for RISC-V IOMMU registers"
> + depends on RISCV_IOMMU
> + help
> + Say Y when the RISC-V IOMMU MMIO window cannot be accessed
> + using naturally aligned 64-bit loads and stores.
> +
> + When enabled, 64-bit IOMMU registers are accessed as paired
> + 32-bit MMIO operations. This option does not describe an RV32
> + kernel or a 32-bit IOMMU architecture.
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index a31f50bbad35..7fa1721b5728 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -53,6 +53,10 @@ struct riscv_iommu_devres {
> void *addr;
> };
>
> +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> +DEFINE_RAW_SPINLOCK(riscv_iommu_32bit_access_lock);
> +#endif
> +
> static void riscv_iommu_devres_pages_release(struct device *dev, void *res)
> {
> struct riscv_iommu_devres *devres = res;
> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> index 46df79dd5495..ba78ef1858c5 100644
> --- a/drivers/iommu/riscv/iommu.h
> +++ b/drivers/iommu/riscv/iommu.h
> @@ -14,6 +14,9 @@
> #include <linux/iommu.h>
> #include <linux/types.h>
> #include <linux/iopoll.h>
> +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> +#include <linux/spinlock.h>
> +#endif
>
> #include "iommu-bits.h"
>
> @@ -69,21 +72,61 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> #define riscv_iommu_readl(iommu, addr) \
> readl_relaxed((iommu)->reg + (addr))
>
> -#define riscv_iommu_readq(iommu, addr) \
> - readq_relaxed((iommu)->reg + (addr))
> -
> #define riscv_iommu_writel(iommu, addr, val) \
> writel_relaxed((val), (iommu)->reg + (addr))
>
> +#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> + readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> + delay_us, timeout_us)
> +
> +#ifndef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> +#define riscv_iommu_readq(iommu, addr) \
> + readq_relaxed((iommu)->reg + (addr))
> +
> #define riscv_iommu_writeq(iommu, addr, val) \
> writeq_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> delay_us, timeout_us)
> +#else /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
>
> -#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> - readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> - delay_us, timeout_us)
> +extern raw_spinlock_t riscv_iommu_32bit_access_lock;
> +
> +static inline u64 __riscv_iommu_readq_relaxed(void __iomem *addr)
> +{
> + u32 lo, hi;
> + unsigned long flags;
> +
> + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> + do {
> + hi = readl_relaxed(addr + sizeof(u32));
> + lo = readl_relaxed(addr);
> + } while (hi != readl_relaxed(addr + sizeof(u32)));
> + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
Why the lock and the loop?
If you need the loop (for places where the hardware might be changing
both words?) then there is no need to read 'hi' twice when you loop.
But does it make any sense to be reading the value either while the hardware
is updating it (does that happen?) or concurrently with a write.
> +
> + return ((u64)hi << 32) | (u64)lo;
> +}
> +
> +static inline void __riscv_iommu_writeq_relaxed(u64 value, void __iomem *addr)
> +{
> + unsigned long flags;
> +
> + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> + writel_relaxed((u32)(value >> 32), addr + sizeof(u32));
> + writel_relaxed((u32)value, addr);
> + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
What are you actually locking against?
I'm pretty sure it doesn't make any sense for two cpu to be writing to the
same entry at the same time.
Can't the writel_relaxed() be re-ordered, so you aren't guaranteeing they'll
be done high-low.
Same with the reads.
David
> +}
> +
> +#define riscv_iommu_readq(iommu, addr) \
> + __riscv_iommu_readq_relaxed((iommu)->reg + (addr))
> +
> +#define riscv_iommu_writeq(iommu, addr, val) \
> + __riscv_iommu_writeq_relaxed((val), (iommu)->reg + (addr))
> +
> +#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> + readx_poll_timeout(__riscv_iommu_readq_relaxed, (iommu)->reg + (addr), \
> + val, cond, delay_us, timeout_us)
> +#endif /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
>
> #endif
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-15 9:59 ` David Laight
0 siblings, 0 replies; 70+ messages in thread
From: David Laight @ 2026-06-15 9:59 UTC (permalink / raw)
To: Zhanpeng Zhang
Cc: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, iommu,
linux-riscv, linux-kernel, Xu Lu, cuiyunhui, yuanzhu
On Mon, 15 Jun 2026 14:48:55 +0800
Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com> wrote:
> Some RISC-V IOMMU implementations cannot perform 64-bit MMIO accesses
> to the IOMMU register file. The RISC-V IOMMU architecture allows 64-bit
> registers to be accessed using 32-bit accesses, provided the accesses are
> properly aligned and do not span multiple registers.
>
> Add a config option for such implementations and access 64-bit IOMMU
> registers as paired 32-bit MMIO operations when it is enabled. Serialize
> the paired accesses so the high and low halves cannot interleave with
> another CPU. Full 64-bit register programming writes the high half before
> the low half.
>
> This option describes the register access width. It is not an RV32 kernel
> mode and does not describe a 32-bit IOMMU architecture.
>
> Co-developed-by: Xu Lu <luxu.kernel@bytedance.com>
> Signed-off-by: Xu Lu <luxu.kernel@bytedance.com>
> Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> ---
> This is needed for platforms whose RISC-V IOMMU register window does not
> support naturally aligned 64-bit MMIO accesses.
>
> drivers/iommu/riscv/Kconfig | 11 ++++++++
> drivers/iommu/riscv/iommu.c | 4 +++
> drivers/iommu/riscv/iommu.h | 55 +++++++++++++++++++++++++++++++++----
> 3 files changed, 64 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/iommu/riscv/Kconfig b/drivers/iommu/riscv/Kconfig
> index b86e5ab94183..54d624b9b2ef 100644
> --- a/drivers/iommu/riscv/Kconfig
> +++ b/drivers/iommu/riscv/Kconfig
> @@ -22,3 +22,14 @@ config RISCV_IOMMU_PCI
> def_bool y if RISCV_IOMMU && PCI_MSI
> help
> Support for the PCIe implementation of RISC-V IOMMU architecture.
> +
> +config RISCV_IOMMU_32BIT_ACCESS
> + bool "Use 32-bit accesses for RISC-V IOMMU registers"
> + depends on RISCV_IOMMU
> + help
> + Say Y when the RISC-V IOMMU MMIO window cannot be accessed
> + using naturally aligned 64-bit loads and stores.
> +
> + When enabled, 64-bit IOMMU registers are accessed as paired
> + 32-bit MMIO operations. This option does not describe an RV32
> + kernel or a 32-bit IOMMU architecture.
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index a31f50bbad35..7fa1721b5728 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -53,6 +53,10 @@ struct riscv_iommu_devres {
> void *addr;
> };
>
> +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> +DEFINE_RAW_SPINLOCK(riscv_iommu_32bit_access_lock);
> +#endif
> +
> static void riscv_iommu_devres_pages_release(struct device *dev, void *res)
> {
> struct riscv_iommu_devres *devres = res;
> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> index 46df79dd5495..ba78ef1858c5 100644
> --- a/drivers/iommu/riscv/iommu.h
> +++ b/drivers/iommu/riscv/iommu.h
> @@ -14,6 +14,9 @@
> #include <linux/iommu.h>
> #include <linux/types.h>
> #include <linux/iopoll.h>
> +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> +#include <linux/spinlock.h>
> +#endif
>
> #include "iommu-bits.h"
>
> @@ -69,21 +72,61 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> #define riscv_iommu_readl(iommu, addr) \
> readl_relaxed((iommu)->reg + (addr))
>
> -#define riscv_iommu_readq(iommu, addr) \
> - readq_relaxed((iommu)->reg + (addr))
> -
> #define riscv_iommu_writel(iommu, addr, val) \
> writel_relaxed((val), (iommu)->reg + (addr))
>
> +#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> + readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> + delay_us, timeout_us)
> +
> +#ifndef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> +#define riscv_iommu_readq(iommu, addr) \
> + readq_relaxed((iommu)->reg + (addr))
> +
> #define riscv_iommu_writeq(iommu, addr, val) \
> writeq_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> delay_us, timeout_us)
> +#else /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
>
> -#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> - readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> - delay_us, timeout_us)
> +extern raw_spinlock_t riscv_iommu_32bit_access_lock;
> +
> +static inline u64 __riscv_iommu_readq_relaxed(void __iomem *addr)
> +{
> + u32 lo, hi;
> + unsigned long flags;
> +
> + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> + do {
> + hi = readl_relaxed(addr + sizeof(u32));
> + lo = readl_relaxed(addr);
> + } while (hi != readl_relaxed(addr + sizeof(u32)));
> + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
Why the lock and the loop?
If you need the loop (for places where the hardware might be changing
both words?) then there is no need to read 'hi' twice when you loop.
But does it make any sense to be reading the value either while the hardware
is updating it (does that happen?) or concurrently with a write.
> +
> + return ((u64)hi << 32) | (u64)lo;
> +}
> +
> +static inline void __riscv_iommu_writeq_relaxed(u64 value, void __iomem *addr)
> +{
> + unsigned long flags;
> +
> + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> + writel_relaxed((u32)(value >> 32), addr + sizeof(u32));
> + writel_relaxed((u32)value, addr);
> + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
What are you actually locking against?
I'm pretty sure it doesn't make any sense for two cpu to be writing to the
same entry at the same time.
Can't the writel_relaxed() be re-ordered, so you aren't guaranteeing they'll
be done high-low.
Same with the reads.
David
> +}
> +
> +#define riscv_iommu_readq(iommu, addr) \
> + __riscv_iommu_readq_relaxed((iommu)->reg + (addr))
> +
> +#define riscv_iommu_writeq(iommu, addr, val) \
> + __riscv_iommu_writeq_relaxed((val), (iommu)->reg + (addr))
> +
> +#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> + readx_poll_timeout(__riscv_iommu_readq_relaxed, (iommu)->reg + (addr), \
> + val, cond, delay_us, timeout_us)
> +#endif /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
>
> #endif
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-15 6:48 ` Zhanpeng Zhang
@ 2026-06-15 12:38 ` Guo Ren
-1 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-15 12:38 UTC (permalink / raw)
To: zhangzhanpeng.jasper
Cc: alex, aou, cuiyunhui, iommu, joro, linux-kernel, linux-riscv,
luxu.kernel, palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
Hi Zhanpeng Zhang,
I noticed you posted a similar fix recently. However, I had already
submitted a similar solution back in September 2025 [1]. It would be
great if you could review it. A few concerns with the current patch:
1. Using spin_lock_irqsave in such a small semantic structure is
unnecessary. IRQ protection should be provided by the higher-level
IOMMU driver interfaces. Many existing call sites already handle IRQ
disabling, so adding it here feels redundant and adds avoidable
overhead.
2. More critically, the RISCV_IOMMU_32BIT_ACCESS Kconfig option is
problematic. According to the RISC-V IOMMU specification (Software
Guidelines, “Reading and writing IOMMU registers”):
> Registers that are 64-bit wide may be accessed using either a 32-bit
> or a 64-bit access.
This clearly requires that any hardware supporting 64-bit MMIO access
must also support 32-bit MMIO access. Therefore, the IOMMU driver should
be built on a 32-bit access base for maximum hardware compatibility.
3. Only performance-monitoring counters require 64-bit IO access or the
high-low-high do-while retry strategy. For ordinary status and control
MMIO registers, a single read is sufficient.
4. Introducing a compile-time Kconfig that forces 32-bit splitting (or
64-bit access) across the entire driver is unnecessary for the vast
majority of registers and would prevent a single kernel Image (or .ko)
from working across all compliant platforms.
I believe the correct approach is to keep the driver on a 32-bit access
foundation (as required by the spec) and apply special 64-bit or
high-low-high handling only to performance-monitoring counters when
justified by data. This preserves broad hardware support without
unnecessary complexity.
What do you think? I’m happy to discuss or collaborate on a cleaner
solution based on the earlier patch [1].
[1]: https://lore.kernel.org/linux-riscv/20250903144217.837448-1-guoren@kernel.org/
Best Regards
GUO Ren
> Some RISC-V IOMMU implementations cannot perform 64-bit MMIO accesses
> to the IOMMU register file. The RISC-V IOMMU architecture allows 64-bit
> registers to be accessed using 32-bit accesses, provided the accesses are
> properly aligned and do not span multiple registers.
>
> Add a config option for such implementations and access 64-bit IOMMU
> registers as paired 32-bit MMIO operations when it is enabled. Serialize
> the paired accesses so the high and low halves cannot interleave with
> another CPU. Full 64-bit register programming writes the high half before
> the low half.
>
> This option describes the register access width. It is not an RV32 kernel
> mode and does not describe a 32-bit IOMMU architecture.
>
> Co-developed-by: Xu Lu <luxu.kernel@bytedance.com>
> Signed-off-by: Xu Lu <luxu.kernel@bytedance.com>
> Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> ---
> This is needed for platforms whose RISC-V IOMMU register window does not
> support naturally aligned 64-bit MMIO accesses.
>
> drivers/iommu/riscv/Kconfig | 11 ++++++++
> drivers/iommu/riscv/iommu.c | 4 +++
> drivers/iommu/riscv/iommu.h | 55 +++++++++++++++++++++++++++++++++----
> 3 files changed, 64 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/iommu/riscv/Kconfig b/drivers/iommu/riscv/Kconfig
> index b86e5ab94183..54d624b9b2ef 100644
> --- a/drivers/iommu/riscv/Kconfig
> +++ b/drivers/iommu/riscv/Kconfig
> @@ -22,3 +22,14 @@ config RISCV_IOMMU_PCI
> def_bool y if RISCV_IOMMU && PCI_MSI
> help
> Support for the PCIe implementation of RISC-V IOMMU architecture.
> +
> +config RISCV_IOMMU_32BIT_ACCESS
> + bool "Use 32-bit accesses for RISC-V IOMMU registers"
> + depends on RISCV_IOMMU
> + help
> + Say Y when the RISC-V IOMMU MMIO window cannot be accessed
> + using naturally aligned 64-bit loads and stores.
> +
> + When enabled, 64-bit IOMMU registers are accessed as paired
> + 32-bit MMIO operations. This option does not describe an RV32
> + kernel or a 32-bit IOMMU architecture.
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index a31f50bbad35..7fa1721b5728 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -53,6 +53,10 @@ struct riscv_iommu_devres {
> void *addr;
> };
>
> +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> +DEFINE_RAW_SPINLOCK(riscv_iommu_32bit_access_lock);
> +#endif
> +
> static void riscv_iommu_devres_pages_release(struct device *dev, void *res)
> {
> struct riscv_iommu_devres *devres = res;
> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> index 46df79dd5495..ba78ef1858c5 100644
> --- a/drivers/iommu/riscv/iommu.h
> +++ b/drivers/iommu/riscv/iommu.h
> @@ -14,6 +14,9 @@
> #include <linux/iommu.h>
> #include <linux/types.h>
> #include <linux/iopoll.h>
> +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> +#include <linux/spinlock.h>
> +#endif
>
> #include "iommu-bits.h"
>
> @@ -69,21 +72,61 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> #define riscv_iommu_readl(iommu, addr) \
> readl_relaxed((iommu)->reg + (addr))
>
> -#define riscv_iommu_readq(iommu, addr) \
> - readq_relaxed((iommu)->reg + (addr))
> -
> #define riscv_iommu_writel(iommu, addr, val) \
> writel_relaxed((val), (iommu)->reg + (addr))
>
> +#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> + readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> + delay_us, timeout_us)
> +
> +#ifndef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> +#define riscv_iommu_readq(iommu, addr) \
> + readq_relaxed((iommu)->reg + (addr))
> +
> #define riscv_iommu_writeq(iommu, addr, val) \
> writeq_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> delay_us, timeout_us)
> +#else /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
>
> -#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> - readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> - delay_us, timeout_us)
> +extern raw_spinlock_t riscv_iommu_32bit_access_lock;
> +
> +static inline u64 __riscv_iommu_readq_relaxed(void __iomem *addr)
> +{
> + u32 lo, hi;
> + unsigned long flags;
> +
> + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> + do {
> + hi = readl_relaxed(addr + sizeof(u32));
> + lo = readl_relaxed(addr);
> + } while (hi != readl_relaxed(addr + sizeof(u32)));
> + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
> +
> + return ((u64)hi << 32) | (u64)lo;
> +}
> +
> +static inline void __riscv_iommu_writeq_relaxed(u64 value, void __iomem *addr)
> +{
> + unsigned long flags;
> +
> + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> + writel_relaxed((u32)(value >> 32), addr + sizeof(u32));
> + writel_relaxed((u32)value, addr);
> + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
> +}
> +
> +#define riscv_iommu_readq(iommu, addr) \
> + __riscv_iommu_readq_relaxed((iommu)->reg + (addr))
> +
> +#define riscv_iommu_writeq(iommu, addr, val) \
> + __riscv_iommu_writeq_relaxed((val), (iommu)->reg + (addr))
> +
> +#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> + readx_poll_timeout(__riscv_iommu_readq_relaxed, (iommu)->reg + (addr), \
> + val, cond, delay_us, timeout_us)
> +#endif /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
>
> #endif
> --
> 2.50.1 (Apple Git-155)
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-15 12:38 ` Guo Ren
0 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-15 12:38 UTC (permalink / raw)
To: zhangzhanpeng.jasper
Cc: alex, aou, cuiyunhui, iommu, joro, linux-kernel, linux-riscv,
luxu.kernel, palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
Hi Zhanpeng Zhang,
I noticed you posted a similar fix recently. However, I had already
submitted a similar solution back in September 2025 [1]. It would be
great if you could review it. A few concerns with the current patch:
1. Using spin_lock_irqsave in such a small semantic structure is
unnecessary. IRQ protection should be provided by the higher-level
IOMMU driver interfaces. Many existing call sites already handle IRQ
disabling, so adding it here feels redundant and adds avoidable
overhead.
2. More critically, the RISCV_IOMMU_32BIT_ACCESS Kconfig option is
problematic. According to the RISC-V IOMMU specification (Software
Guidelines, “Reading and writing IOMMU registers”):
> Registers that are 64-bit wide may be accessed using either a 32-bit
> or a 64-bit access.
This clearly requires that any hardware supporting 64-bit MMIO access
must also support 32-bit MMIO access. Therefore, the IOMMU driver should
be built on a 32-bit access base for maximum hardware compatibility.
3. Only performance-monitoring counters require 64-bit IO access or the
high-low-high do-while retry strategy. For ordinary status and control
MMIO registers, a single read is sufficient.
4. Introducing a compile-time Kconfig that forces 32-bit splitting (or
64-bit access) across the entire driver is unnecessary for the vast
majority of registers and would prevent a single kernel Image (or .ko)
from working across all compliant platforms.
I believe the correct approach is to keep the driver on a 32-bit access
foundation (as required by the spec) and apply special 64-bit or
high-low-high handling only to performance-monitoring counters when
justified by data. This preserves broad hardware support without
unnecessary complexity.
What do you think? I’m happy to discuss or collaborate on a cleaner
solution based on the earlier patch [1].
[1]: https://lore.kernel.org/linux-riscv/20250903144217.837448-1-guoren@kernel.org/
Best Regards
GUO Ren
> Some RISC-V IOMMU implementations cannot perform 64-bit MMIO accesses
> to the IOMMU register file. The RISC-V IOMMU architecture allows 64-bit
> registers to be accessed using 32-bit accesses, provided the accesses are
> properly aligned and do not span multiple registers.
>
> Add a config option for such implementations and access 64-bit IOMMU
> registers as paired 32-bit MMIO operations when it is enabled. Serialize
> the paired accesses so the high and low halves cannot interleave with
> another CPU. Full 64-bit register programming writes the high half before
> the low half.
>
> This option describes the register access width. It is not an RV32 kernel
> mode and does not describe a 32-bit IOMMU architecture.
>
> Co-developed-by: Xu Lu <luxu.kernel@bytedance.com>
> Signed-off-by: Xu Lu <luxu.kernel@bytedance.com>
> Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> ---
> This is needed for platforms whose RISC-V IOMMU register window does not
> support naturally aligned 64-bit MMIO accesses.
>
> drivers/iommu/riscv/Kconfig | 11 ++++++++
> drivers/iommu/riscv/iommu.c | 4 +++
> drivers/iommu/riscv/iommu.h | 55 +++++++++++++++++++++++++++++++++----
> 3 files changed, 64 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/iommu/riscv/Kconfig b/drivers/iommu/riscv/Kconfig
> index b86e5ab94183..54d624b9b2ef 100644
> --- a/drivers/iommu/riscv/Kconfig
> +++ b/drivers/iommu/riscv/Kconfig
> @@ -22,3 +22,14 @@ config RISCV_IOMMU_PCI
> def_bool y if RISCV_IOMMU && PCI_MSI
> help
> Support for the PCIe implementation of RISC-V IOMMU architecture.
> +
> +config RISCV_IOMMU_32BIT_ACCESS
> + bool "Use 32-bit accesses for RISC-V IOMMU registers"
> + depends on RISCV_IOMMU
> + help
> + Say Y when the RISC-V IOMMU MMIO window cannot be accessed
> + using naturally aligned 64-bit loads and stores.
> +
> + When enabled, 64-bit IOMMU registers are accessed as paired
> + 32-bit MMIO operations. This option does not describe an RV32
> + kernel or a 32-bit IOMMU architecture.
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index a31f50bbad35..7fa1721b5728 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -53,6 +53,10 @@ struct riscv_iommu_devres {
> void *addr;
> };
>
> +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> +DEFINE_RAW_SPINLOCK(riscv_iommu_32bit_access_lock);
> +#endif
> +
> static void riscv_iommu_devres_pages_release(struct device *dev, void *res)
> {
> struct riscv_iommu_devres *devres = res;
> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> index 46df79dd5495..ba78ef1858c5 100644
> --- a/drivers/iommu/riscv/iommu.h
> +++ b/drivers/iommu/riscv/iommu.h
> @@ -14,6 +14,9 @@
> #include <linux/iommu.h>
> #include <linux/types.h>
> #include <linux/iopoll.h>
> +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> +#include <linux/spinlock.h>
> +#endif
>
> #include "iommu-bits.h"
>
> @@ -69,21 +72,61 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> #define riscv_iommu_readl(iommu, addr) \
> readl_relaxed((iommu)->reg + (addr))
>
> -#define riscv_iommu_readq(iommu, addr) \
> - readq_relaxed((iommu)->reg + (addr))
> -
> #define riscv_iommu_writel(iommu, addr, val) \
> writel_relaxed((val), (iommu)->reg + (addr))
>
> +#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> + readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> + delay_us, timeout_us)
> +
> +#ifndef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> +#define riscv_iommu_readq(iommu, addr) \
> + readq_relaxed((iommu)->reg + (addr))
> +
> #define riscv_iommu_writeq(iommu, addr, val) \
> writeq_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> delay_us, timeout_us)
> +#else /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
>
> -#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> - readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> - delay_us, timeout_us)
> +extern raw_spinlock_t riscv_iommu_32bit_access_lock;
> +
> +static inline u64 __riscv_iommu_readq_relaxed(void __iomem *addr)
> +{
> + u32 lo, hi;
> + unsigned long flags;
> +
> + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> + do {
> + hi = readl_relaxed(addr + sizeof(u32));
> + lo = readl_relaxed(addr);
> + } while (hi != readl_relaxed(addr + sizeof(u32)));
> + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
> +
> + return ((u64)hi << 32) | (u64)lo;
> +}
> +
> +static inline void __riscv_iommu_writeq_relaxed(u64 value, void __iomem *addr)
> +{
> + unsigned long flags;
> +
> + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> + writel_relaxed((u32)(value >> 32), addr + sizeof(u32));
> + writel_relaxed((u32)value, addr);
> + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
> +}
> +
> +#define riscv_iommu_readq(iommu, addr) \
> + __riscv_iommu_readq_relaxed((iommu)->reg + (addr))
> +
> +#define riscv_iommu_writeq(iommu, addr, val) \
> + __riscv_iommu_writeq_relaxed((val), (iommu)->reg + (addr))
> +
> +#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> + readx_poll_timeout(__riscv_iommu_readq_relaxed, (iommu)->reg + (addr), \
> + val, cond, delay_us, timeout_us)
> +#endif /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
>
> #endif
> --
> 2.50.1 (Apple Git-155)
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [External] Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-15 9:59 ` David Laight
@ 2026-06-15 13:21 ` Zhanpeng Zhang
-1 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-06-15 13:21 UTC (permalink / raw)
To: David Laight
Cc: tjeznach, joro, will, robin.murphy, pjw, palmer, aou, alex, iommu,
linux-riscv, linux-kernel, Xu Lu, cuiyunhui, yuanzhu,
guoren@kernel.org
Hi David,
Thanks, I agree with your point about the lock and the relaxed accessors.
Guo Ren also pointed me to the earlier discussion on this topic, and I think the
right next step is to align with that thread rather than continue this separate
Kconfig-based patch.
So I will drop this patch.
Thanks,
Zhanpeng
> From: "David Laight"<david.laight.linux@gmail.com>
> Date: Mon, Jun 15, 2026, 18:00
> Subject: [External] Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
> To: "Zhanpeng Zhang"<zhangzhanpeng.jasper@bytedance.com>
> Cc: "Tomasz Jeznach"<tjeznach@rivosinc.com>, "Joerg Roedel"<joro@8bytes.org>, "Will Deacon"<will@kernel.org>, "Robin Murphy"<robin.murphy@arm.com>, "Paul Walmsley"<pjw@kernel.org>, "Palmer Dabbelt"<palmer@dabbelt.com>, "Albert Ou"<aou@eecs.berkeley.edu>, "Alexandre Ghiti"<alex@ghiti.fr>, <iommu@lists.linux.dev>, <linux-riscv@lists.infradead.org>, <linux-kernel@vger.kernel.org>, "Xu Lu"<luxu.kernel@bytedance.com>, <cuiyunhui@bytedance.com>, <yuanzhu@bytedance.com>
> On Mon, 15 Jun 2026 14:48:55 +0800
> Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com> wrote:
>
> > Some RISC-V IOMMU implementations cannot perform 64-bit MMIO accesses
> > to the IOMMU register file. The RISC-V IOMMU architecture allows 64-bit
> > registers to be accessed using 32-bit accesses, provided the accesses are
> > properly aligned and do not span multiple registers.
> >
> > Add a config option for such implementations and access 64-bit IOMMU
> > registers as paired 32-bit MMIO operations when it is enabled. Serialize
> > the paired accesses so the high and low halves cannot interleave with
> > another CPU. Full 64-bit register programming writes the high half before
> > the low half.
> >
> > This option describes the register access width. It is not an RV32 kernel
> > mode and does not describe a 32-bit IOMMU architecture.
> >
> > Co-developed-by: Xu Lu <luxu.kernel@bytedance.com>
> > Signed-off-by: Xu Lu <luxu.kernel@bytedance.com>
> > Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> > ---
> > This is needed for platforms whose RISC-V IOMMU register window does not
> > support naturally aligned 64-bit MMIO accesses.
> >
> > drivers/iommu/riscv/Kconfig | 11 ++++++++
> > drivers/iommu/riscv/iommu.c | 4 +++
> > drivers/iommu/riscv/iommu.h | 55 +++++++++++++++++++++++++++++++++----
> > 3 files changed, 64 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/iommu/riscv/Kconfig b/drivers/iommu/riscv/Kconfig
> > index b86e5ab94183..54d624b9b2ef 100644
> > --- a/drivers/iommu/riscv/Kconfig
> > +++ b/drivers/iommu/riscv/Kconfig
> > @@ -22,3 +22,14 @@ config RISCV_IOMMU_PCI
> > def_bool y if RISCV_IOMMU && PCI_MSI
> > help
> > Support for the PCIe implementation of RISC-V IOMMU architecture.
> > +
> > +config RISCV_IOMMU_32BIT_ACCESS
> > + bool "Use 32-bit accesses for RISC-V IOMMU registers"
> > + depends on RISCV_IOMMU
> > + help
> > + Say Y when the RISC-V IOMMU MMIO window cannot be accessed
> > + using naturally aligned 64-bit loads and stores.
> > +
> > + When enabled, 64-bit IOMMU registers are accessed as paired
> > + 32-bit MMIO operations. This option does not describe an RV32
> > + kernel or a 32-bit IOMMU architecture.
> > diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> > index a31f50bbad35..7fa1721b5728 100644
> > --- a/drivers/iommu/riscv/iommu.c
> > +++ b/drivers/iommu/riscv/iommu.c
> > @@ -53,6 +53,10 @@ struct riscv_iommu_devres {
> > void *addr;
> > };
> >
> > +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> > +DEFINE_RAW_SPINLOCK(riscv_iommu_32bit_access_lock);
> > +#endif
> > +
> > static void riscv_iommu_devres_pages_release(struct device *dev, void *res)
> > {
> > struct riscv_iommu_devres *devres = res;
> > diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> > index 46df79dd5495..ba78ef1858c5 100644
> > --- a/drivers/iommu/riscv/iommu.h
> > +++ b/drivers/iommu/riscv/iommu.h
> > @@ -14,6 +14,9 @@
> > #include <linux/iommu.h>
> > #include <linux/types.h>
> > #include <linux/iopoll.h>
> > +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> > +#include <linux/spinlock.h>
> > +#endif
> >
> > #include "iommu-bits.h"
> >
> > @@ -69,21 +72,61 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> > #define riscv_iommu_readl(iommu, addr) \
> > readl_relaxed((iommu)->reg + (addr))
> >
> > -#define riscv_iommu_readq(iommu, addr) \
> > - readq_relaxed((iommu)->reg + (addr))
> > -
> > #define riscv_iommu_writel(iommu, addr, val) \
> > writel_relaxed((val), (iommu)->reg + (addr))
> >
> > +#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > + readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> > + delay_us, timeout_us)
> > +
> > +#ifndef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> > +#define riscv_iommu_readq(iommu, addr) \
> > + readq_relaxed((iommu)->reg + (addr))
> > +
> > #define riscv_iommu_writeq(iommu, addr, val) \
> > writeq_relaxed((val), (iommu)->reg + (addr))
> >
> > #define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> > delay_us, timeout_us)
> > +#else /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
> >
> > -#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > - readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> > - delay_us, timeout_us)
> > +extern raw_spinlock_t riscv_iommu_32bit_access_lock;
> > +
> > +static inline u64 __riscv_iommu_readq_relaxed(void __iomem *addr)
> > +{
> > + u32 lo, hi;
> > + unsigned long flags;
> > +
> > + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> > + do {
> > + hi = readl_relaxed(addr + sizeof(u32));
> > + lo = readl_relaxed(addr);
> > + } while (hi != readl_relaxed(addr + sizeof(u32)));
> > + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
>
> Why the lock and the loop?
> If you need the loop (for places where the hardware might be changing
> both words?) then there is no need to read 'hi' twice when you loop.
>
> But does it make any sense to be reading the value either while the hardware
> is updating it (does that happen?) or concurrently with a write.
>
>
> > +
> > + return ((u64)hi << 32) | (u64)lo;
> > +}
> > +
> > +static inline void __riscv_iommu_writeq_relaxed(u64 value, void __iomem *addr)
> > +{
> > + unsigned long flags;
> > +
> > + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> > + writel_relaxed((u32)(value >> 32), addr + sizeof(u32));
> > + writel_relaxed((u32)value, addr);
> > + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
>
> What are you actually locking against?
> I'm pretty sure it doesn't make any sense for two cpu to be writing to the
> same entry at the same time.
>
> Can't the writel_relaxed() be re-ordered, so you aren't guaranteeing they'll
> be done high-low.
> Same with the reads.
>
> David
>
> > +}
> > +
> > +#define riscv_iommu_readq(iommu, addr) \
> > + __riscv_iommu_readq_relaxed((iommu)->reg + (addr))
> > +
> > +#define riscv_iommu_writeq(iommu, addr, val) \
> > + __riscv_iommu_writeq_relaxed((val), (iommu)->reg + (addr))
> > +
> > +#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > + readx_poll_timeout(__riscv_iommu_readq_relaxed, (iommu)->reg + (addr), \
> > + val, cond, delay_us, timeout_us)
> > +#endif /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
> >
> > #endif
>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [External] Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-15 13:21 ` Zhanpeng Zhang
0 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-06-15 13:21 UTC (permalink / raw)
To: David Laight
Cc: tjeznach, joro, will, robin.murphy, pjw, palmer, aou, alex, iommu,
linux-riscv, linux-kernel, Xu Lu, cuiyunhui, yuanzhu,
guoren@kernel.org
Hi David,
Thanks, I agree with your point about the lock and the relaxed accessors.
Guo Ren also pointed me to the earlier discussion on this topic, and I think the
right next step is to align with that thread rather than continue this separate
Kconfig-based patch.
So I will drop this patch.
Thanks,
Zhanpeng
> From: "David Laight"<david.laight.linux@gmail.com>
> Date: Mon, Jun 15, 2026, 18:00
> Subject: [External] Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
> To: "Zhanpeng Zhang"<zhangzhanpeng.jasper@bytedance.com>
> Cc: "Tomasz Jeznach"<tjeznach@rivosinc.com>, "Joerg Roedel"<joro@8bytes.org>, "Will Deacon"<will@kernel.org>, "Robin Murphy"<robin.murphy@arm.com>, "Paul Walmsley"<pjw@kernel.org>, "Palmer Dabbelt"<palmer@dabbelt.com>, "Albert Ou"<aou@eecs.berkeley.edu>, "Alexandre Ghiti"<alex@ghiti.fr>, <iommu@lists.linux.dev>, <linux-riscv@lists.infradead.org>, <linux-kernel@vger.kernel.org>, "Xu Lu"<luxu.kernel@bytedance.com>, <cuiyunhui@bytedance.com>, <yuanzhu@bytedance.com>
> On Mon, 15 Jun 2026 14:48:55 +0800
> Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com> wrote:
>
> > Some RISC-V IOMMU implementations cannot perform 64-bit MMIO accesses
> > to the IOMMU register file. The RISC-V IOMMU architecture allows 64-bit
> > registers to be accessed using 32-bit accesses, provided the accesses are
> > properly aligned and do not span multiple registers.
> >
> > Add a config option for such implementations and access 64-bit IOMMU
> > registers as paired 32-bit MMIO operations when it is enabled. Serialize
> > the paired accesses so the high and low halves cannot interleave with
> > another CPU. Full 64-bit register programming writes the high half before
> > the low half.
> >
> > This option describes the register access width. It is not an RV32 kernel
> > mode and does not describe a 32-bit IOMMU architecture.
> >
> > Co-developed-by: Xu Lu <luxu.kernel@bytedance.com>
> > Signed-off-by: Xu Lu <luxu.kernel@bytedance.com>
> > Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> > ---
> > This is needed for platforms whose RISC-V IOMMU register window does not
> > support naturally aligned 64-bit MMIO accesses.
> >
> > drivers/iommu/riscv/Kconfig | 11 ++++++++
> > drivers/iommu/riscv/iommu.c | 4 +++
> > drivers/iommu/riscv/iommu.h | 55 +++++++++++++++++++++++++++++++++----
> > 3 files changed, 64 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/iommu/riscv/Kconfig b/drivers/iommu/riscv/Kconfig
> > index b86e5ab94183..54d624b9b2ef 100644
> > --- a/drivers/iommu/riscv/Kconfig
> > +++ b/drivers/iommu/riscv/Kconfig
> > @@ -22,3 +22,14 @@ config RISCV_IOMMU_PCI
> > def_bool y if RISCV_IOMMU && PCI_MSI
> > help
> > Support for the PCIe implementation of RISC-V IOMMU architecture.
> > +
> > +config RISCV_IOMMU_32BIT_ACCESS
> > + bool "Use 32-bit accesses for RISC-V IOMMU registers"
> > + depends on RISCV_IOMMU
> > + help
> > + Say Y when the RISC-V IOMMU MMIO window cannot be accessed
> > + using naturally aligned 64-bit loads and stores.
> > +
> > + When enabled, 64-bit IOMMU registers are accessed as paired
> > + 32-bit MMIO operations. This option does not describe an RV32
> > + kernel or a 32-bit IOMMU architecture.
> > diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> > index a31f50bbad35..7fa1721b5728 100644
> > --- a/drivers/iommu/riscv/iommu.c
> > +++ b/drivers/iommu/riscv/iommu.c
> > @@ -53,6 +53,10 @@ struct riscv_iommu_devres {
> > void *addr;
> > };
> >
> > +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> > +DEFINE_RAW_SPINLOCK(riscv_iommu_32bit_access_lock);
> > +#endif
> > +
> > static void riscv_iommu_devres_pages_release(struct device *dev, void *res)
> > {
> > struct riscv_iommu_devres *devres = res;
> > diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> > index 46df79dd5495..ba78ef1858c5 100644
> > --- a/drivers/iommu/riscv/iommu.h
> > +++ b/drivers/iommu/riscv/iommu.h
> > @@ -14,6 +14,9 @@
> > #include <linux/iommu.h>
> > #include <linux/types.h>
> > #include <linux/iopoll.h>
> > +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> > +#include <linux/spinlock.h>
> > +#endif
> >
> > #include "iommu-bits.h"
> >
> > @@ -69,21 +72,61 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> > #define riscv_iommu_readl(iommu, addr) \
> > readl_relaxed((iommu)->reg + (addr))
> >
> > -#define riscv_iommu_readq(iommu, addr) \
> > - readq_relaxed((iommu)->reg + (addr))
> > -
> > #define riscv_iommu_writel(iommu, addr, val) \
> > writel_relaxed((val), (iommu)->reg + (addr))
> >
> > +#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > + readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> > + delay_us, timeout_us)
> > +
> > +#ifndef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> > +#define riscv_iommu_readq(iommu, addr) \
> > + readq_relaxed((iommu)->reg + (addr))
> > +
> > #define riscv_iommu_writeq(iommu, addr, val) \
> > writeq_relaxed((val), (iommu)->reg + (addr))
> >
> > #define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> > delay_us, timeout_us)
> > +#else /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
> >
> > -#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > - readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> > - delay_us, timeout_us)
> > +extern raw_spinlock_t riscv_iommu_32bit_access_lock;
> > +
> > +static inline u64 __riscv_iommu_readq_relaxed(void __iomem *addr)
> > +{
> > + u32 lo, hi;
> > + unsigned long flags;
> > +
> > + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> > + do {
> > + hi = readl_relaxed(addr + sizeof(u32));
> > + lo = readl_relaxed(addr);
> > + } while (hi != readl_relaxed(addr + sizeof(u32)));
> > + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
>
> Why the lock and the loop?
> If you need the loop (for places where the hardware might be changing
> both words?) then there is no need to read 'hi' twice when you loop.
>
> But does it make any sense to be reading the value either while the hardware
> is updating it (does that happen?) or concurrently with a write.
>
>
> > +
> > + return ((u64)hi << 32) | (u64)lo;
> > +}
> > +
> > +static inline void __riscv_iommu_writeq_relaxed(u64 value, void __iomem *addr)
> > +{
> > + unsigned long flags;
> > +
> > + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> > + writel_relaxed((u32)(value >> 32), addr + sizeof(u32));
> > + writel_relaxed((u32)value, addr);
> > + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
>
> What are you actually locking against?
> I'm pretty sure it doesn't make any sense for two cpu to be writing to the
> same entry at the same time.
>
> Can't the writel_relaxed() be re-ordered, so you aren't guaranteeing they'll
> be done high-low.
> Same with the reads.
>
> David
>
> > +}
> > +
> > +#define riscv_iommu_readq(iommu, addr) \
> > + __riscv_iommu_readq_relaxed((iommu)->reg + (addr))
> > +
> > +#define riscv_iommu_writeq(iommu, addr, val) \
> > + __riscv_iommu_writeq_relaxed((val), (iommu)->reg + (addr))
> > +
> > +#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > + readx_poll_timeout(__riscv_iommu_readq_relaxed, (iommu)->reg + (addr), \
> > + val, cond, delay_us, timeout_us)
> > +#endif /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
> >
> > #endif
>
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [External] Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-15 12:38 ` Guo Ren
@ 2026-06-15 13:23 ` Zhanpeng Zhang
-1 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-06-15 13:23 UTC (permalink / raw)
To: Guo Ren
Cc: alex, aou, cuiyunhui, iommu, joro, linux-kernel, linux-riscv,
luxu.kernel, palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
Hi Guo Ren,
Thanks for the pointer, and sorry that I missed the earlier discussion.
I agree with your concerns. The lock in my v1 is not the right approach, and
David also pointed out the same issue. More importantly, after reading your
comments, I agree that this should not be handled as a separate Kconfig-selected
path.
If the RISC-V IOMMU specification allows 64-bit registers to be accessed with
32-bit MMIO operations, then a generic kernel should be able to support that
without requiring a platform-specific build-time option. So I will drop my v1
and will not continue this separate patch.
I will take a closer look at your earlier proposal and the previous discussion.
If the preferred direction is to make 32-bit register accesses part of the
driver's common register access model, I am happy to help test, review, or
rework patches in that direction.
Best Regards,
Zhanpeng
> From: "Guo Ren"<guoren@kernel.org>
> Date: Mon, Jun 15, 2026, 20:38
> Subject: [External] Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
> To: <zhangzhanpeng.jasper@bytedance.com>
> Cc: <alex@ghiti.fr>, <aou@eecs.berkeley.edu>, <cuiyunhui@bytedance.com>, <iommu@lists.linux.dev>, <joro@8bytes.org>, <linux-kernel@vger.kernel.org>, <linux-riscv@lists.infradead.org>, <luxu.kernel@bytedance.com>, <palmer@dabbelt.com>, <pjw@kernel.org>, <robin.murphy@arm.com>, <tjeznach@rivosinc.com>, <will@kernel.org>, <yuanzhu@bytedance.com>
> Hi Zhanpeng Zhang,
>
> I noticed you posted a similar fix recently. However, I had already
> submitted a similar solution back in September 2025 [1]. It would be
> great if you could review it. A few concerns with the current patch:
>
> 1. Using spin_lock_irqsave in such a small semantic structure is
> unnecessary. IRQ protection should be provided by the higher-level
> IOMMU driver interfaces. Many existing call sites already handle IRQ
> disabling, so adding it here feels redundant and adds avoidable
> overhead.
>
> 2. More critically, the RISCV_IOMMU_32BIT_ACCESS Kconfig option is
> problematic. According to the RISC-V IOMMU specification (Software
> Guidelines, “Reading and writing IOMMU registers”):
>
> > Registers that are 64-bit wide may be accessed using either a 32-bit
> > or a 64-bit access.
>
> This clearly requires that any hardware supporting 64-bit MMIO access
> must also support 32-bit MMIO access. Therefore, the IOMMU driver should
> be built on a 32-bit access base for maximum hardware compatibility.
>
> 3. Only performance-monitoring counters require 64-bit IO access or the
> high-low-high do-while retry strategy. For ordinary status and control
> MMIO registers, a single read is sufficient.
>
> 4. Introducing a compile-time Kconfig that forces 32-bit splitting (or
> 64-bit access) across the entire driver is unnecessary for the vast
> majority of registers and would prevent a single kernel Image (or .ko)
> from working across all compliant platforms.
>
> I believe the correct approach is to keep the driver on a 32-bit access
> foundation (as required by the spec) and apply special 64-bit or
> high-low-high handling only to performance-monitoring counters when
> justified by data. This preserves broad hardware support without
> unnecessary complexity.
>
> What do you think? I’m happy to discuss or collaborate on a cleaner
> solution based on the earlier patch [1].
>
> [1]: https://lore.kernel.org/linux-riscv/20250903144217.837448-1-guoren@kernel.org/
>
> Best Regards
> GUO Ren
>
> > Some RISC-V IOMMU implementations cannot perform 64-bit MMIO accesses
> > to the IOMMU register file. The RISC-V IOMMU architecture allows 64-bit
> > registers to be accessed using 32-bit accesses, provided the accesses are
> > properly aligned and do not span multiple registers.
> >
> > Add a config option for such implementations and access 64-bit IOMMU
> > registers as paired 32-bit MMIO operations when it is enabled. Serialize
> > the paired accesses so the high and low halves cannot interleave with
> > another CPU. Full 64-bit register programming writes the high half before
> > the low half.
> >
> > This option describes the register access width. It is not an RV32 kernel
> > mode and does not describe a 32-bit IOMMU architecture.
> >
> > Co-developed-by: Xu Lu <luxu.kernel@bytedance.com>
> > Signed-off-by: Xu Lu <luxu.kernel@bytedance.com>
> > Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> > ---
> > This is needed for platforms whose RISC-V IOMMU register window does not
> > support naturally aligned 64-bit MMIO accesses.
> >
> > drivers/iommu/riscv/Kconfig | 11 ++++++++
> > drivers/iommu/riscv/iommu.c | 4 +++
> > drivers/iommu/riscv/iommu.h | 55 +++++++++++++++++++++++++++++++++----
> > 3 files changed, 64 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/iommu/riscv/Kconfig b/drivers/iommu/riscv/Kconfig
> > index b86e5ab94183..54d624b9b2ef 100644
> > --- a/drivers/iommu/riscv/Kconfig
> > +++ b/drivers/iommu/riscv/Kconfig
> > @@ -22,3 +22,14 @@ config RISCV_IOMMU_PCI
> > def_bool y if RISCV_IOMMU && PCI_MSI
> > help
> > Support for the PCIe implementation of RISC-V IOMMU architecture.
> > +
> > +config RISCV_IOMMU_32BIT_ACCESS
> > + bool "Use 32-bit accesses for RISC-V IOMMU registers"
> > + depends on RISCV_IOMMU
> > + help
> > + Say Y when the RISC-V IOMMU MMIO window cannot be accessed
> > + using naturally aligned 64-bit loads and stores.
> > +
> > + When enabled, 64-bit IOMMU registers are accessed as paired
> > + 32-bit MMIO operations. This option does not describe an RV32
> > + kernel or a 32-bit IOMMU architecture.
> > diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> > index a31f50bbad35..7fa1721b5728 100644
> > --- a/drivers/iommu/riscv/iommu.c
> > +++ b/drivers/iommu/riscv/iommu.c
> > @@ -53,6 +53,10 @@ struct riscv_iommu_devres {
> > void *addr;
> > };
> >
> > +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> > +DEFINE_RAW_SPINLOCK(riscv_iommu_32bit_access_lock);
> > +#endif
> > +
> > static void riscv_iommu_devres_pages_release(struct device *dev, void *res)
> > {
> > struct riscv_iommu_devres *devres = res;
> > diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> > index 46df79dd5495..ba78ef1858c5 100644
> > --- a/drivers/iommu/riscv/iommu.h
> > +++ b/drivers/iommu/riscv/iommu.h
> > @@ -14,6 +14,9 @@
> > #include <linux/iommu.h>
> > #include <linux/types.h>
> > #include <linux/iopoll.h>
> > +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> > +#include <linux/spinlock.h>
> > +#endif
> >
> > #include "iommu-bits.h"
> >
> > @@ -69,21 +72,61 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> > #define riscv_iommu_readl(iommu, addr) \
> > readl_relaxed((iommu)->reg + (addr))
> >
> > -#define riscv_iommu_readq(iommu, addr) \
> > - readq_relaxed((iommu)->reg + (addr))
> > -
> > #define riscv_iommu_writel(iommu, addr, val) \
> > writel_relaxed((val), (iommu)->reg + (addr))
> >
> > +#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > + readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> > + delay_us, timeout_us)
> > +
> > +#ifndef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> > +#define riscv_iommu_readq(iommu, addr) \
> > + readq_relaxed((iommu)->reg + (addr))
> > +
> > #define riscv_iommu_writeq(iommu, addr, val) \
> > writeq_relaxed((val), (iommu)->reg + (addr))
> >
> > #define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> > delay_us, timeout_us)
> > +#else /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
> >
> > -#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > - readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> > - delay_us, timeout_us)
> > +extern raw_spinlock_t riscv_iommu_32bit_access_lock;
> > +
> > +static inline u64 __riscv_iommu_readq_relaxed(void __iomem *addr)
> > +{
> > + u32 lo, hi;
> > + unsigned long flags;
> > +
> > + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> > + do {
> > + hi = readl_relaxed(addr + sizeof(u32));
> > + lo = readl_relaxed(addr);
> > + } while (hi != readl_relaxed(addr + sizeof(u32)));
> > + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
> > +
> > + return ((u64)hi << 32) | (u64)lo;
> > +}
> > +
> > +static inline void __riscv_iommu_writeq_relaxed(u64 value, void __iomem *addr)
> > +{
> > + unsigned long flags;
> > +
> > + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> > + writel_relaxed((u32)(value >> 32), addr + sizeof(u32));
> > + writel_relaxed((u32)value, addr);
> > + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
> > +}
> > +
> > +#define riscv_iommu_readq(iommu, addr) \
> > + __riscv_iommu_readq_relaxed((iommu)->reg + (addr))
> > +
> > +#define riscv_iommu_writeq(iommu, addr, val) \
> > + __riscv_iommu_writeq_relaxed((val), (iommu)->reg + (addr))
> > +
> > +#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > + readx_poll_timeout(__riscv_iommu_readq_relaxed, (iommu)->reg + (addr), \
> > + val, cond, delay_us, timeout_us)
> > +#endif /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
> >
> > #endif
> > --
> > 2.50.1 (Apple Git-155)
>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [External] Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-15 13:23 ` Zhanpeng Zhang
0 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-06-15 13:23 UTC (permalink / raw)
To: Guo Ren
Cc: alex, aou, cuiyunhui, iommu, joro, linux-kernel, linux-riscv,
luxu.kernel, palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
Hi Guo Ren,
Thanks for the pointer, and sorry that I missed the earlier discussion.
I agree with your concerns. The lock in my v1 is not the right approach, and
David also pointed out the same issue. More importantly, after reading your
comments, I agree that this should not be handled as a separate Kconfig-selected
path.
If the RISC-V IOMMU specification allows 64-bit registers to be accessed with
32-bit MMIO operations, then a generic kernel should be able to support that
without requiring a platform-specific build-time option. So I will drop my v1
and will not continue this separate patch.
I will take a closer look at your earlier proposal and the previous discussion.
If the preferred direction is to make 32-bit register accesses part of the
driver's common register access model, I am happy to help test, review, or
rework patches in that direction.
Best Regards,
Zhanpeng
> From: "Guo Ren"<guoren@kernel.org>
> Date: Mon, Jun 15, 2026, 20:38
> Subject: [External] Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
> To: <zhangzhanpeng.jasper@bytedance.com>
> Cc: <alex@ghiti.fr>, <aou@eecs.berkeley.edu>, <cuiyunhui@bytedance.com>, <iommu@lists.linux.dev>, <joro@8bytes.org>, <linux-kernel@vger.kernel.org>, <linux-riscv@lists.infradead.org>, <luxu.kernel@bytedance.com>, <palmer@dabbelt.com>, <pjw@kernel.org>, <robin.murphy@arm.com>, <tjeznach@rivosinc.com>, <will@kernel.org>, <yuanzhu@bytedance.com>
> Hi Zhanpeng Zhang,
>
> I noticed you posted a similar fix recently. However, I had already
> submitted a similar solution back in September 2025 [1]. It would be
> great if you could review it. A few concerns with the current patch:
>
> 1. Using spin_lock_irqsave in such a small semantic structure is
> unnecessary. IRQ protection should be provided by the higher-level
> IOMMU driver interfaces. Many existing call sites already handle IRQ
> disabling, so adding it here feels redundant and adds avoidable
> overhead.
>
> 2. More critically, the RISCV_IOMMU_32BIT_ACCESS Kconfig option is
> problematic. According to the RISC-V IOMMU specification (Software
> Guidelines, “Reading and writing IOMMU registers”):
>
> > Registers that are 64-bit wide may be accessed using either a 32-bit
> > or a 64-bit access.
>
> This clearly requires that any hardware supporting 64-bit MMIO access
> must also support 32-bit MMIO access. Therefore, the IOMMU driver should
> be built on a 32-bit access base for maximum hardware compatibility.
>
> 3. Only performance-monitoring counters require 64-bit IO access or the
> high-low-high do-while retry strategy. For ordinary status and control
> MMIO registers, a single read is sufficient.
>
> 4. Introducing a compile-time Kconfig that forces 32-bit splitting (or
> 64-bit access) across the entire driver is unnecessary for the vast
> majority of registers and would prevent a single kernel Image (or .ko)
> from working across all compliant platforms.
>
> I believe the correct approach is to keep the driver on a 32-bit access
> foundation (as required by the spec) and apply special 64-bit or
> high-low-high handling only to performance-monitoring counters when
> justified by data. This preserves broad hardware support without
> unnecessary complexity.
>
> What do you think? I’m happy to discuss or collaborate on a cleaner
> solution based on the earlier patch [1].
>
> [1]: https://lore.kernel.org/linux-riscv/20250903144217.837448-1-guoren@kernel.org/
>
> Best Regards
> GUO Ren
>
> > Some RISC-V IOMMU implementations cannot perform 64-bit MMIO accesses
> > to the IOMMU register file. The RISC-V IOMMU architecture allows 64-bit
> > registers to be accessed using 32-bit accesses, provided the accesses are
> > properly aligned and do not span multiple registers.
> >
> > Add a config option for such implementations and access 64-bit IOMMU
> > registers as paired 32-bit MMIO operations when it is enabled. Serialize
> > the paired accesses so the high and low halves cannot interleave with
> > another CPU. Full 64-bit register programming writes the high half before
> > the low half.
> >
> > This option describes the register access width. It is not an RV32 kernel
> > mode and does not describe a 32-bit IOMMU architecture.
> >
> > Co-developed-by: Xu Lu <luxu.kernel@bytedance.com>
> > Signed-off-by: Xu Lu <luxu.kernel@bytedance.com>
> > Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> > ---
> > This is needed for platforms whose RISC-V IOMMU register window does not
> > support naturally aligned 64-bit MMIO accesses.
> >
> > drivers/iommu/riscv/Kconfig | 11 ++++++++
> > drivers/iommu/riscv/iommu.c | 4 +++
> > drivers/iommu/riscv/iommu.h | 55 +++++++++++++++++++++++++++++++++----
> > 3 files changed, 64 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/iommu/riscv/Kconfig b/drivers/iommu/riscv/Kconfig
> > index b86e5ab94183..54d624b9b2ef 100644
> > --- a/drivers/iommu/riscv/Kconfig
> > +++ b/drivers/iommu/riscv/Kconfig
> > @@ -22,3 +22,14 @@ config RISCV_IOMMU_PCI
> > def_bool y if RISCV_IOMMU && PCI_MSI
> > help
> > Support for the PCIe implementation of RISC-V IOMMU architecture.
> > +
> > +config RISCV_IOMMU_32BIT_ACCESS
> > + bool "Use 32-bit accesses for RISC-V IOMMU registers"
> > + depends on RISCV_IOMMU
> > + help
> > + Say Y when the RISC-V IOMMU MMIO window cannot be accessed
> > + using naturally aligned 64-bit loads and stores.
> > +
> > + When enabled, 64-bit IOMMU registers are accessed as paired
> > + 32-bit MMIO operations. This option does not describe an RV32
> > + kernel or a 32-bit IOMMU architecture.
> > diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> > index a31f50bbad35..7fa1721b5728 100644
> > --- a/drivers/iommu/riscv/iommu.c
> > +++ b/drivers/iommu/riscv/iommu.c
> > @@ -53,6 +53,10 @@ struct riscv_iommu_devres {
> > void *addr;
> > };
> >
> > +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> > +DEFINE_RAW_SPINLOCK(riscv_iommu_32bit_access_lock);
> > +#endif
> > +
> > static void riscv_iommu_devres_pages_release(struct device *dev, void *res)
> > {
> > struct riscv_iommu_devres *devres = res;
> > diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> > index 46df79dd5495..ba78ef1858c5 100644
> > --- a/drivers/iommu/riscv/iommu.h
> > +++ b/drivers/iommu/riscv/iommu.h
> > @@ -14,6 +14,9 @@
> > #include <linux/iommu.h>
> > #include <linux/types.h>
> > #include <linux/iopoll.h>
> > +#ifdef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> > +#include <linux/spinlock.h>
> > +#endif
> >
> > #include "iommu-bits.h"
> >
> > @@ -69,21 +72,61 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> > #define riscv_iommu_readl(iommu, addr) \
> > readl_relaxed((iommu)->reg + (addr))
> >
> > -#define riscv_iommu_readq(iommu, addr) \
> > - readq_relaxed((iommu)->reg + (addr))
> > -
> > #define riscv_iommu_writel(iommu, addr, val) \
> > writel_relaxed((val), (iommu)->reg + (addr))
> >
> > +#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > + readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> > + delay_us, timeout_us)
> > +
> > +#ifndef CONFIG_RISCV_IOMMU_32BIT_ACCESS
> > +#define riscv_iommu_readq(iommu, addr) \
> > + readq_relaxed((iommu)->reg + (addr))
> > +
> > #define riscv_iommu_writeq(iommu, addr, val) \
> > writeq_relaxed((val), (iommu)->reg + (addr))
> >
> > #define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> > delay_us, timeout_us)
> > +#else /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
> >
> > -#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > - readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> > - delay_us, timeout_us)
> > +extern raw_spinlock_t riscv_iommu_32bit_access_lock;
> > +
> > +static inline u64 __riscv_iommu_readq_relaxed(void __iomem *addr)
> > +{
> > + u32 lo, hi;
> > + unsigned long flags;
> > +
> > + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> > + do {
> > + hi = readl_relaxed(addr + sizeof(u32));
> > + lo = readl_relaxed(addr);
> > + } while (hi != readl_relaxed(addr + sizeof(u32)));
> > + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
> > +
> > + return ((u64)hi << 32) | (u64)lo;
> > +}
> > +
> > +static inline void __riscv_iommu_writeq_relaxed(u64 value, void __iomem *addr)
> > +{
> > + unsigned long flags;
> > +
> > + raw_spin_lock_irqsave(&riscv_iommu_32bit_access_lock, flags);
> > + writel_relaxed((u32)(value >> 32), addr + sizeof(u32));
> > + writel_relaxed((u32)value, addr);
> > + raw_spin_unlock_irqrestore(&riscv_iommu_32bit_access_lock, flags);
> > +}
> > +
> > +#define riscv_iommu_readq(iommu, addr) \
> > + __riscv_iommu_readq_relaxed((iommu)->reg + (addr))
> > +
> > +#define riscv_iommu_writeq(iommu, addr, val) \
> > + __riscv_iommu_writeq_relaxed((val), (iommu)->reg + (addr))
> > +
> > +#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > + readx_poll_timeout(__riscv_iommu_readq_relaxed, (iommu)->reg + (addr), \
> > + val, cond, delay_us, timeout_us)
> > +#endif /* CONFIG_RISCV_IOMMU_32BIT_ACCESS */
> >
> > #endif
> > --
> > 2.50.1 (Apple Git-155)
>
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-15 12:38 ` Guo Ren
@ 2026-06-16 10:36 ` David Laight
-1 siblings, 0 replies; 70+ messages in thread
From: David Laight @ 2026-06-16 10:36 UTC (permalink / raw)
To: Guo Ren
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On Mon, 15 Jun 2026 12:38:17 +0000
Guo Ren <guoren@kernel.org> wrote:
> Hi Zhanpeng Zhang,
..
> 3. Only performance-monitoring counters require 64-bit IO access or the
> high-low-high do-while retry strategy. For ordinary status and control
> MMIO registers, a single read is sufficient.
Actually this sequence should be enough for a counter:
hi = read_hi();
lo = read_lo();
if (hi != read_hi()) {
// Pick a value that happened while doing the reads.
hi++;
lo = 0;
}
-- David
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-16 10:36 ` David Laight
0 siblings, 0 replies; 70+ messages in thread
From: David Laight @ 2026-06-16 10:36 UTC (permalink / raw)
To: Guo Ren
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On Mon, 15 Jun 2026 12:38:17 +0000
Guo Ren <guoren@kernel.org> wrote:
> Hi Zhanpeng Zhang,
..
> 3. Only performance-monitoring counters require 64-bit IO access or the
> high-low-high do-while retry strategy. For ordinary status and control
> MMIO registers, a single read is sufficient.
Actually this sequence should be enough for a counter:
hi = read_hi();
lo = read_lo();
if (hi != read_hi()) {
// Pick a value that happened while doing the reads.
hi++;
lo = 0;
}
-- David
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-16 10:36 ` David Laight
@ 2026-06-16 15:47 ` Guo Ren
-1 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-16 15:47 UTC (permalink / raw)
To: David Laight
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On Tue, Jun 16, 2026 at 6:36 PM David Laight
<david.laight.linux@gmail.com> wrote:
>
> On Mon, 15 Jun 2026 12:38:17 +0000
> Guo Ren <guoren@kernel.org> wrote:
>
> > Hi Zhanpeng Zhang,
> ..
> > 3. Only performance-monitoring counters require 64-bit IO access or the
> > high-low-high do-while retry strategy. For ordinary status and control
> > MMIO registers, a single read is sufficient.
>
> Actually this sequence should be enough for a counter:
> hi = read_hi();
> lo = read_lo();
> if (hi != read_hi()) {
> // Pick a value that happened while doing the reads.
> hi++;
> lo = 0;
> }
This is not a free optimization — it does not preserve the same
semantics as an atomic 64-bit read. There are at least two correctness
issues:
1. Loss of precision: If hi changed during the read, setting lo = 0
discards the actual lo value. The correct approach is to re-read lo
after detecting the change, not fabricate a value.
2. Multiple overflows: This assumes at most one overflow occurs
between reads. If two overflows happen (e.g., due to interrupt
injection), hi++ will produce an incorrect result, silently corrupting
the counter value.
Negligible benefit: hi changing between reads is an extremely rare
event. Optimizing away the retry loop for such a rare case provides no
meaningful performance gain. And if hi never stabilizes, that
indicates a hardware failure — in which case hanging in the retry loop
is actually the more appropriate behavior, as it makes the failure
visible rather than silently producing garbage values.
The high-low-high do-while retry strategy exists precisely to handle
these cases correctly. I don't think this proposed sequence is a valid
replacement.
--
Best Regards
Guo Ren
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-16 15:47 ` Guo Ren
0 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-16 15:47 UTC (permalink / raw)
To: David Laight
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On Tue, Jun 16, 2026 at 6:36 PM David Laight
<david.laight.linux@gmail.com> wrote:
>
> On Mon, 15 Jun 2026 12:38:17 +0000
> Guo Ren <guoren@kernel.org> wrote:
>
> > Hi Zhanpeng Zhang,
> ..
> > 3. Only performance-monitoring counters require 64-bit IO access or the
> > high-low-high do-while retry strategy. For ordinary status and control
> > MMIO registers, a single read is sufficient.
>
> Actually this sequence should be enough for a counter:
> hi = read_hi();
> lo = read_lo();
> if (hi != read_hi()) {
> // Pick a value that happened while doing the reads.
> hi++;
> lo = 0;
> }
This is not a free optimization — it does not preserve the same
semantics as an atomic 64-bit read. There are at least two correctness
issues:
1. Loss of precision: If hi changed during the read, setting lo = 0
discards the actual lo value. The correct approach is to re-read lo
after detecting the change, not fabricate a value.
2. Multiple overflows: This assumes at most one overflow occurs
between reads. If two overflows happen (e.g., due to interrupt
injection), hi++ will produce an incorrect result, silently corrupting
the counter value.
Negligible benefit: hi changing between reads is an extremely rare
event. Optimizing away the retry loop for such a rare case provides no
meaningful performance gain. And if hi never stabilizes, that
indicates a hardware failure — in which case hanging in the retry loop
is actually the more appropriate behavior, as it makes the failure
visible rather than silently producing garbage values.
The high-low-high do-while retry strategy exists precisely to handle
these cases correctly. I don't think this proposed sequence is a valid
replacement.
--
Best Regards
Guo Ren
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-16 15:47 ` Guo Ren
@ 2026-06-16 19:51 ` David Laight
-1 siblings, 0 replies; 70+ messages in thread
From: David Laight @ 2026-06-16 19:51 UTC (permalink / raw)
To: Guo Ren
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On Tue, 16 Jun 2026 23:47:05 +0800
Guo Ren <guoren@kernel.org> wrote:
> On Tue, Jun 16, 2026 at 6:36 PM David Laight
> <david.laight.linux@gmail.com> wrote:
> >
> > On Mon, 15 Jun 2026 12:38:17 +0000
> > Guo Ren <guoren@kernel.org> wrote:
> >
> > > Hi Zhanpeng Zhang,
> > ..
> > > 3. Only performance-monitoring counters require 64-bit IO access or the
> > > high-low-high do-while retry strategy. For ordinary status and control
> > > MMIO registers, a single read is sufficient.
> >
> > Actually this sequence should be enough for a counter:
> > hi = read_hi();
> > lo = read_lo();
> > if (hi != read_hi()) {
> > // Pick a value that happened while doing the reads.
> > hi++;
> > lo = 0;
> > }
> This is not a free optimization — it does not preserve the same
> semantics as an atomic 64-bit read. There are at least two correctness
> issues:
>
> 1. Loss of precision: If hi changed during the read, setting lo = 0
> discards the actual lo value. The correct approach is to re-read lo
> after detecting the change, not fabricate a value.
>
> 2. Multiple overflows: This assumes at most one overflow occurs
> between reads. If two overflows happen (e.g., due to interrupt
> injection), hi++ will produce an incorrect result, silently corrupting
> the counter value.
>
> Negligible benefit: hi changing between reads is an extremely rare
> event. Optimizing away the retry loop for such a rare case provides no
> meaningful performance gain. And if hi never stabilizes, that
> indicates a hardware failure — in which case hanging in the retry loop
> is actually the more appropriate behavior, as it makes the failure
> visible rather than silently producing garbage values.
>
> The high-low-high do-while retry strategy exists precisely to handle
> these cases correctly. I don't think this proposed sequence is a valid
> replacement.
>
The point is that if the value is an incrementing counter (of some form)
then the value is stale by the time the reading sequence completes.
So all the code can be assumed to do is return a value the counter had
sometime between when the code started and when it finished.
If hi changes then hi+1:0 must have happened while the code was running
so it is a safe return value.
It is likely that the reads are also much slower than memory reads.
If hi changes relatively infrequently (compared to the number of reads)
it may be worth saving the previously read value and avoiding the second
read of hi if it is the same as the previous read hi and lo has got larger.
David
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-16 19:51 ` David Laight
0 siblings, 0 replies; 70+ messages in thread
From: David Laight @ 2026-06-16 19:51 UTC (permalink / raw)
To: Guo Ren
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On Tue, 16 Jun 2026 23:47:05 +0800
Guo Ren <guoren@kernel.org> wrote:
> On Tue, Jun 16, 2026 at 6:36 PM David Laight
> <david.laight.linux@gmail.com> wrote:
> >
> > On Mon, 15 Jun 2026 12:38:17 +0000
> > Guo Ren <guoren@kernel.org> wrote:
> >
> > > Hi Zhanpeng Zhang,
> > ..
> > > 3. Only performance-monitoring counters require 64-bit IO access or the
> > > high-low-high do-while retry strategy. For ordinary status and control
> > > MMIO registers, a single read is sufficient.
> >
> > Actually this sequence should be enough for a counter:
> > hi = read_hi();
> > lo = read_lo();
> > if (hi != read_hi()) {
> > // Pick a value that happened while doing the reads.
> > hi++;
> > lo = 0;
> > }
> This is not a free optimization — it does not preserve the same
> semantics as an atomic 64-bit read. There are at least two correctness
> issues:
>
> 1. Loss of precision: If hi changed during the read, setting lo = 0
> discards the actual lo value. The correct approach is to re-read lo
> after detecting the change, not fabricate a value.
>
> 2. Multiple overflows: This assumes at most one overflow occurs
> between reads. If two overflows happen (e.g., due to interrupt
> injection), hi++ will produce an incorrect result, silently corrupting
> the counter value.
>
> Negligible benefit: hi changing between reads is an extremely rare
> event. Optimizing away the retry loop for such a rare case provides no
> meaningful performance gain. And if hi never stabilizes, that
> indicates a hardware failure — in which case hanging in the retry loop
> is actually the more appropriate behavior, as it makes the failure
> visible rather than silently producing garbage values.
>
> The high-low-high do-while retry strategy exists precisely to handle
> these cases correctly. I don't think this proposed sequence is a valid
> replacement.
>
The point is that if the value is an incrementing counter (of some form)
then the value is stale by the time the reading sequence completes.
So all the code can be assumed to do is return a value the counter had
sometime between when the code started and when it finished.
If hi changes then hi+1:0 must have happened while the code was running
so it is a safe return value.
It is likely that the reads are also much slower than memory reads.
If hi changes relatively infrequently (compared to the number of reads)
it may be worth saving the previously read value and avoiding the second
read of hi if it is the same as the previous read hi and lo has got larger.
David
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-16 19:51 ` David Laight
@ 2026-06-17 16:24 ` Guo Ren
-1 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-17 16:24 UTC (permalink / raw)
To: David Laight
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On Wed, Jun 17, 2026 at 3:51 AM David Laight
<david.laight.linux@gmail.com> wrote:
>
> On Tue, 16 Jun 2026 23:47:05 +0800
> Guo Ren <guoren@kernel.org> wrote:
>
> > On Tue, Jun 16, 2026 at 6:36 PM David Laight
> > <david.laight.linux@gmail.com> wrote:
> > >
> > > On Mon, 15 Jun 2026 12:38:17 +0000
> > > Guo Ren <guoren@kernel.org> wrote:
> > >
> > > > Hi Zhanpeng Zhang,
> > > ..
> > > > 3. Only performance-monitoring counters require 64-bit IO access or the
> > > > high-low-high do-while retry strategy. For ordinary status and control
> > > > MMIO registers, a single read is sufficient.
> > >
> > > Actually this sequence should be enough for a counter:
> > > hi = read_hi();
> > > lo = read_lo();
> > > if (hi != read_hi()) {
> > > // Pick a value that happened while doing the reads.
> > > hi++;
> > > lo = 0;
> > > }
> > This is not a free optimization — it does not preserve the same
> > semantics as an atomic 64-bit read. There are at least two correctness
> > issues:
> >
> > 1. Loss of precision: If hi changed during the read, setting lo = 0
> > discards the actual lo value. The correct approach is to re-read lo
> > after detecting the change, not fabricate a value.
> >
> > 2. Multiple overflows: This assumes at most one overflow occurs
> > between reads. If two overflows happen (e.g., due to interrupt
> > injection), hi++ will produce an incorrect result, silently corrupting
> > the counter value.
> >
> > Negligible benefit: hi changing between reads is an extremely rare
> > event. Optimizing away the retry loop for such a rare case provides no
> > meaningful performance gain. And if hi never stabilizes, that
> > indicates a hardware failure — in which case hanging in the retry loop
> > is actually the more appropriate behavior, as it makes the failure
> > visible rather than silently producing garbage values.
> >
> > The high-low-high do-while retry strategy exists precisely to handle
> > these cases correctly. I don't think this proposed sequence is a valid
> > replacement.
> >
>
> The point is that if the value is an incrementing counter (of some form)
> then the value is stale by the time the reading sequence completes.
> So all the code can be assumed to do is return a value the counter had
> sometime between when the code started and when it finished.
> If hi changes then hi+1:0 must have happened while the code was running
> so it is a safe return value.
You're right — (hi + 1):0 is enough for hi-lo-hi. Thank you for
sticking to your point; it helped me think more carefully and
understand the subtlety.
>
> It is likely that the reads are also much slower than memory reads.
> If hi changes relatively infrequently (compared to the number of reads)
> it may be worth saving the previously read value and avoiding the second
> read of hi if it is the same as the previous read hi and lo has got larger.
--
Best Regards
Guo Ren
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-17 16:24 ` Guo Ren
0 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-17 16:24 UTC (permalink / raw)
To: David Laight
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On Wed, Jun 17, 2026 at 3:51 AM David Laight
<david.laight.linux@gmail.com> wrote:
>
> On Tue, 16 Jun 2026 23:47:05 +0800
> Guo Ren <guoren@kernel.org> wrote:
>
> > On Tue, Jun 16, 2026 at 6:36 PM David Laight
> > <david.laight.linux@gmail.com> wrote:
> > >
> > > On Mon, 15 Jun 2026 12:38:17 +0000
> > > Guo Ren <guoren@kernel.org> wrote:
> > >
> > > > Hi Zhanpeng Zhang,
> > > ..
> > > > 3. Only performance-monitoring counters require 64-bit IO access or the
> > > > high-low-high do-while retry strategy. For ordinary status and control
> > > > MMIO registers, a single read is sufficient.
> > >
> > > Actually this sequence should be enough for a counter:
> > > hi = read_hi();
> > > lo = read_lo();
> > > if (hi != read_hi()) {
> > > // Pick a value that happened while doing the reads.
> > > hi++;
> > > lo = 0;
> > > }
> > This is not a free optimization — it does not preserve the same
> > semantics as an atomic 64-bit read. There are at least two correctness
> > issues:
> >
> > 1. Loss of precision: If hi changed during the read, setting lo = 0
> > discards the actual lo value. The correct approach is to re-read lo
> > after detecting the change, not fabricate a value.
> >
> > 2. Multiple overflows: This assumes at most one overflow occurs
> > between reads. If two overflows happen (e.g., due to interrupt
> > injection), hi++ will produce an incorrect result, silently corrupting
> > the counter value.
> >
> > Negligible benefit: hi changing between reads is an extremely rare
> > event. Optimizing away the retry loop for such a rare case provides no
> > meaningful performance gain. And if hi never stabilizes, that
> > indicates a hardware failure — in which case hanging in the retry loop
> > is actually the more appropriate behavior, as it makes the failure
> > visible rather than silently producing garbage values.
> >
> > The high-low-high do-while retry strategy exists precisely to handle
> > these cases correctly. I don't think this proposed sequence is a valid
> > replacement.
> >
>
> The point is that if the value is an incrementing counter (of some form)
> then the value is stale by the time the reading sequence completes.
> So all the code can be assumed to do is return a value the counter had
> sometime between when the code started and when it finished.
> If hi changes then hi+1:0 must have happened while the code was running
> so it is a safe return value.
You're right — (hi + 1):0 is enough for hi-lo-hi. Thank you for
sticking to your point; it helped me think more carefully and
understand the subtlety.
>
> It is likely that the reads are also much slower than memory reads.
> If hi changes relatively infrequently (compared to the number of reads)
> it may be worth saving the previously read value and avoiding the second
> read of hi if it is the same as the previous read hi and lo has got larger.
--
Best Regards
Guo Ren
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-17 16:24 ` Guo Ren
@ 2026-06-17 21:54 ` David Laight
-1 siblings, 0 replies; 70+ messages in thread
From: David Laight @ 2026-06-17 21:54 UTC (permalink / raw)
To: Guo Ren
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On Thu, 18 Jun 2026 00:24:12 +0800
Guo Ren <guoren@kernel.org> wrote:
> On Wed, Jun 17, 2026 at 3:51 AM David Laight
> <david.laight.linux@gmail.com> wrote:
> >
> > On Tue, 16 Jun 2026 23:47:05 +0800
> > Guo Ren <guoren@kernel.org> wrote:
> >
> > > On Tue, Jun 16, 2026 at 6:36 PM David Laight
> > > <david.laight.linux@gmail.com> wrote:
> > > >
> > > > On Mon, 15 Jun 2026 12:38:17 +0000
> > > > Guo Ren <guoren@kernel.org> wrote:
> > > >
> > > > > Hi Zhanpeng Zhang,
> > > > ..
> > > > > 3. Only performance-monitoring counters require 64-bit IO access or the
> > > > > high-low-high do-while retry strategy. For ordinary status and control
> > > > > MMIO registers, a single read is sufficient.
> > > >
> > > > Actually this sequence should be enough for a counter:
> > > > hi = read_hi();
> > > > lo = read_lo();
> > > > if (hi != read_hi()) {
> > > > // Pick a value that happened while doing the reads.
> > > > hi++;
> > > > lo = 0;
> > > > }
> > > This is not a free optimization — it does not preserve the same
> > > semantics as an atomic 64-bit read. There are at least two correctness
> > > issues:
> > >
> > > 1. Loss of precision: If hi changed during the read, setting lo = 0
> > > discards the actual lo value. The correct approach is to re-read lo
> > > after detecting the change, not fabricate a value.
> > >
> > > 2. Multiple overflows: This assumes at most one overflow occurs
> > > between reads. If two overflows happen (e.g., due to interrupt
> > > injection), hi++ will produce an incorrect result, silently corrupting
> > > the counter value.
> > >
> > > Negligible benefit: hi changing between reads is an extremely rare
> > > event. Optimizing away the retry loop for such a rare case provides no
> > > meaningful performance gain. And if hi never stabilizes, that
> > > indicates a hardware failure — in which case hanging in the retry loop
> > > is actually the more appropriate behavior, as it makes the failure
> > > visible rather than silently producing garbage values.
> > >
> > > The high-low-high do-while retry strategy exists precisely to handle
> > > these cases correctly. I don't think this proposed sequence is a valid
> > > replacement.
> > >
> >
> > The point is that if the value is an incrementing counter (of some form)
> > then the value is stale by the time the reading sequence completes.
> > So all the code can be assumed to do is return a value the counter had
> > sometime between when the code started and when it finished.
> > If hi changes then hi+1:0 must have happened while the code was running
> > so it is a safe return value.
> You're right — (hi + 1):0 is enough for hi-lo-hi. Thank you for
> sticking to your point; it helped me think more carefully and
> understand the subtlety.
Don't worry, I'll have written the loop in the past.
Probably best is read old_hi-lo-hi then if (hi != old_hi) lo = 0
David
>
> >
> > It is likely that the reads are also much slower than memory reads.
> > If hi changes relatively infrequently (compared to the number of reads)
> > it may be worth saving the previously read value and avoiding the second
> > read of hi if it is the same as the previous read hi and lo has got larger.
>
>
> --
> Best Regards
> Guo Ren
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-17 21:54 ` David Laight
0 siblings, 0 replies; 70+ messages in thread
From: David Laight @ 2026-06-17 21:54 UTC (permalink / raw)
To: Guo Ren
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On Thu, 18 Jun 2026 00:24:12 +0800
Guo Ren <guoren@kernel.org> wrote:
> On Wed, Jun 17, 2026 at 3:51 AM David Laight
> <david.laight.linux@gmail.com> wrote:
> >
> > On Tue, 16 Jun 2026 23:47:05 +0800
> > Guo Ren <guoren@kernel.org> wrote:
> >
> > > On Tue, Jun 16, 2026 at 6:36 PM David Laight
> > > <david.laight.linux@gmail.com> wrote:
> > > >
> > > > On Mon, 15 Jun 2026 12:38:17 +0000
> > > > Guo Ren <guoren@kernel.org> wrote:
> > > >
> > > > > Hi Zhanpeng Zhang,
> > > > ..
> > > > > 3. Only performance-monitoring counters require 64-bit IO access or the
> > > > > high-low-high do-while retry strategy. For ordinary status and control
> > > > > MMIO registers, a single read is sufficient.
> > > >
> > > > Actually this sequence should be enough for a counter:
> > > > hi = read_hi();
> > > > lo = read_lo();
> > > > if (hi != read_hi()) {
> > > > // Pick a value that happened while doing the reads.
> > > > hi++;
> > > > lo = 0;
> > > > }
> > > This is not a free optimization — it does not preserve the same
> > > semantics as an atomic 64-bit read. There are at least two correctness
> > > issues:
> > >
> > > 1. Loss of precision: If hi changed during the read, setting lo = 0
> > > discards the actual lo value. The correct approach is to re-read lo
> > > after detecting the change, not fabricate a value.
> > >
> > > 2. Multiple overflows: This assumes at most one overflow occurs
> > > between reads. If two overflows happen (e.g., due to interrupt
> > > injection), hi++ will produce an incorrect result, silently corrupting
> > > the counter value.
> > >
> > > Negligible benefit: hi changing between reads is an extremely rare
> > > event. Optimizing away the retry loop for such a rare case provides no
> > > meaningful performance gain. And if hi never stabilizes, that
> > > indicates a hardware failure — in which case hanging in the retry loop
> > > is actually the more appropriate behavior, as it makes the failure
> > > visible rather than silently producing garbage values.
> > >
> > > The high-low-high do-while retry strategy exists precisely to handle
> > > these cases correctly. I don't think this proposed sequence is a valid
> > > replacement.
> > >
> >
> > The point is that if the value is an incrementing counter (of some form)
> > then the value is stale by the time the reading sequence completes.
> > So all the code can be assumed to do is return a value the counter had
> > sometime between when the code started and when it finished.
> > If hi changes then hi+1:0 must have happened while the code was running
> > so it is a safe return value.
> You're right — (hi + 1):0 is enough for hi-lo-hi. Thank you for
> sticking to your point; it helped me think more carefully and
> understand the subtlety.
Don't worry, I'll have written the loop in the past.
Probably best is read old_hi-lo-hi then if (hi != old_hi) lo = 0
David
>
> >
> > It is likely that the reads are also much slower than memory reads.
> > If hi changes relatively infrequently (compared to the number of reads)
> > it may be worth saving the previously read value and avoiding the second
> > read of hi if it is the same as the previous read hi and lo has got larger.
>
>
> --
> Best Regards
> Guo Ren
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-15 12:38 ` Guo Ren
@ 2026-06-18 3:20 ` Vivian Wang
-1 siblings, 0 replies; 70+ messages in thread
From: Vivian Wang @ 2026-06-18 3:20 UTC (permalink / raw)
To: Guo Ren, zhangzhanpeng.jasper
Cc: alex, aou, cuiyunhui, iommu, joro, linux-kernel, linux-riscv,
luxu.kernel, palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
On 6/15/26 20:38, Guo Ren wrote:
> Hi Zhanpeng Zhang,
>
> I noticed you posted a similar fix recently. However, I had already
> submitted a similar solution back in September 2025 [1]. It would be
> great if you could review it. A few concerns with the current patch:
>
> 1. Using spin_lock_irqsave in such a small semantic structure is
> unnecessary. IRQ protection should be provided by the higher-level
> IOMMU driver interfaces. Many existing call sites already handle IRQ
> disabling, so adding it here feels redundant and adds avoidable
> overhead.
>
> 2. More critically, the RISCV_IOMMU_32BIT_ACCESS Kconfig option is
> problematic. According to the RISC-V IOMMU specification (Software
> Guidelines, “Reading and writing IOMMU registers”):
>
>> Registers that are 64-bit wide may be accessed using either a 32-bit
>> or a 64-bit access.
> This clearly requires that any hardware supporting 64-bit MMIO access
> must also support 32-bit MMIO access. Therefore, the IOMMU driver should
> be built on a 32-bit access base for maximum hardware compatibility.
I agree that the driver should be build with 32-bit accesses to support
these hardware. However there's one minor thing that I want to propose
regarding this, for the sake of clarity:
RISC-V IOMMU implementations that do not support 64-bit accesses are not
fully compliant with the RISC-V IOMMU specification and should not use
the standard RISC-V identifiers, namely:
- Devicetree compatible riscv,iommu
- ACPI HID RSCV0004
This, in my opinion, is not only the right thing to do (not claiming to
be standard hardware without actually being standard hardware), but also
saves a lot of trouble for operating system developers (yes, there are
RISC-V operating systems other than Linux out there), since invalid IO
accesses usually manifest as mysterious access faults (that would
usually suggest an incorrect IOMMU address), or worse, system hangs.
I would appreciate if the hardware vendors shipping 32-bit-access-only
IOMMUs would consider this.
Vivian "dramforever" Wang
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-18 3:20 ` Vivian Wang
0 siblings, 0 replies; 70+ messages in thread
From: Vivian Wang @ 2026-06-18 3:20 UTC (permalink / raw)
To: Guo Ren, zhangzhanpeng.jasper
Cc: alex, aou, cuiyunhui, iommu, joro, linux-kernel, linux-riscv,
luxu.kernel, palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
On 6/15/26 20:38, Guo Ren wrote:
> Hi Zhanpeng Zhang,
>
> I noticed you posted a similar fix recently. However, I had already
> submitted a similar solution back in September 2025 [1]. It would be
> great if you could review it. A few concerns with the current patch:
>
> 1. Using spin_lock_irqsave in such a small semantic structure is
> unnecessary. IRQ protection should be provided by the higher-level
> IOMMU driver interfaces. Many existing call sites already handle IRQ
> disabling, so adding it here feels redundant and adds avoidable
> overhead.
>
> 2. More critically, the RISCV_IOMMU_32BIT_ACCESS Kconfig option is
> problematic. According to the RISC-V IOMMU specification (Software
> Guidelines, “Reading and writing IOMMU registers”):
>
>> Registers that are 64-bit wide may be accessed using either a 32-bit
>> or a 64-bit access.
> This clearly requires that any hardware supporting 64-bit MMIO access
> must also support 32-bit MMIO access. Therefore, the IOMMU driver should
> be built on a 32-bit access base for maximum hardware compatibility.
I agree that the driver should be build with 32-bit accesses to support
these hardware. However there's one minor thing that I want to propose
regarding this, for the sake of clarity:
RISC-V IOMMU implementations that do not support 64-bit accesses are not
fully compliant with the RISC-V IOMMU specification and should not use
the standard RISC-V identifiers, namely:
- Devicetree compatible riscv,iommu
- ACPI HID RSCV0004
This, in my opinion, is not only the right thing to do (not claiming to
be standard hardware without actually being standard hardware), but also
saves a lot of trouble for operating system developers (yes, there are
RISC-V operating systems other than Linux out there), since invalid IO
accesses usually manifest as mysterious access faults (that would
usually suggest an incorrect IOMMU address), or worse, system hangs.
I would appreciate if the hardware vendors shipping 32-bit-access-only
IOMMUs would consider this.
Vivian "dramforever" Wang
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-17 21:54 ` David Laight
@ 2026-06-18 3:36 ` Guo Ren
-1 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-18 3:36 UTC (permalink / raw)
To: David Laight
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On Thu, Jun 18, 2026 at 5:54 AM David Laight
<david.laight.linux@gmail.com> wrote:
>
> On Thu, 18 Jun 2026 00:24:12 +0800
> Guo Ren <guoren@kernel.org> wrote:
>
> > On Wed, Jun 17, 2026 at 3:51 AM David Laight
> > <david.laight.linux@gmail.com> wrote:
> > >
> > > On Tue, 16 Jun 2026 23:47:05 +0800
> > > Guo Ren <guoren@kernel.org> wrote:
> > >
> > > > On Tue, Jun 16, 2026 at 6:36 PM David Laight
> > > > <david.laight.linux@gmail.com> wrote:
> > > > >
> > > > > On Mon, 15 Jun 2026 12:38:17 +0000
> > > > > Guo Ren <guoren@kernel.org> wrote:
> > > > >
> > > > > > Hi Zhanpeng Zhang,
> > > > > ..
> > > > > > 3. Only performance-monitoring counters require 64-bit IO access or the
> > > > > > high-low-high do-while retry strategy. For ordinary status and control
> > > > > > MMIO registers, a single read is sufficient.
> > > > >
> > > > > Actually this sequence should be enough for a counter:
> > > > > hi = read_hi();
> > > > > lo = read_lo();
> > > > > if (hi != read_hi()) {
> > > > > // Pick a value that happened while doing the reads.
> > > > > hi++;
> > > > > lo = 0;
> > > > > }
> > > > This is not a free optimization — it does not preserve the same
> > > > semantics as an atomic 64-bit read. There are at least two correctness
> > > > issues:
> > > >
> > > > 1. Loss of precision: If hi changed during the read, setting lo = 0
> > > > discards the actual lo value. The correct approach is to re-read lo
> > > > after detecting the change, not fabricate a value.
> > > >
> > > > 2. Multiple overflows: This assumes at most one overflow occurs
> > > > between reads. If two overflows happen (e.g., due to interrupt
> > > > injection), hi++ will produce an incorrect result, silently corrupting
> > > > the counter value.
> > > >
> > > > Negligible benefit: hi changing between reads is an extremely rare
> > > > event. Optimizing away the retry loop for such a rare case provides no
> > > > meaningful performance gain. And if hi never stabilizes, that
> > > > indicates a hardware failure — in which case hanging in the retry loop
> > > > is actually the more appropriate behavior, as it makes the failure
> > > > visible rather than silently producing garbage values.
> > > >
> > > > The high-low-high do-while retry strategy exists precisely to handle
> > > > these cases correctly. I don't think this proposed sequence is a valid
> > > > replacement.
> > > >
> > >
> > > The point is that if the value is an incrementing counter (of some form)
> > > then the value is stale by the time the reading sequence completes.
> > > So all the code can be assumed to do is return a value the counter had
> > > sometime between when the code started and when it finished.
> > > If hi changes then hi+1:0 must have happened while the code was running
> > > so it is a safe return value.
> > You're right — (hi + 1):0 is enough for hi-lo-hi. Thank you for
> > sticking to your point; it helped me think more carefully and
> > understand the subtlety.
>
> Don't worry, I'll have written the loop in the past.
> Probably best is read old_hi-lo-hi then if (hi != old_hi) lo = 0
This is more conservative than (hi + 1):0.
--
Best Regards
Guo Ren
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-18 3:36 ` Guo Ren
0 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-18 3:36 UTC (permalink / raw)
To: David Laight
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On Thu, Jun 18, 2026 at 5:54 AM David Laight
<david.laight.linux@gmail.com> wrote:
>
> On Thu, 18 Jun 2026 00:24:12 +0800
> Guo Ren <guoren@kernel.org> wrote:
>
> > On Wed, Jun 17, 2026 at 3:51 AM David Laight
> > <david.laight.linux@gmail.com> wrote:
> > >
> > > On Tue, 16 Jun 2026 23:47:05 +0800
> > > Guo Ren <guoren@kernel.org> wrote:
> > >
> > > > On Tue, Jun 16, 2026 at 6:36 PM David Laight
> > > > <david.laight.linux@gmail.com> wrote:
> > > > >
> > > > > On Mon, 15 Jun 2026 12:38:17 +0000
> > > > > Guo Ren <guoren@kernel.org> wrote:
> > > > >
> > > > > > Hi Zhanpeng Zhang,
> > > > > ..
> > > > > > 3. Only performance-monitoring counters require 64-bit IO access or the
> > > > > > high-low-high do-while retry strategy. For ordinary status and control
> > > > > > MMIO registers, a single read is sufficient.
> > > > >
> > > > > Actually this sequence should be enough for a counter:
> > > > > hi = read_hi();
> > > > > lo = read_lo();
> > > > > if (hi != read_hi()) {
> > > > > // Pick a value that happened while doing the reads.
> > > > > hi++;
> > > > > lo = 0;
> > > > > }
> > > > This is not a free optimization — it does not preserve the same
> > > > semantics as an atomic 64-bit read. There are at least two correctness
> > > > issues:
> > > >
> > > > 1. Loss of precision: If hi changed during the read, setting lo = 0
> > > > discards the actual lo value. The correct approach is to re-read lo
> > > > after detecting the change, not fabricate a value.
> > > >
> > > > 2. Multiple overflows: This assumes at most one overflow occurs
> > > > between reads. If two overflows happen (e.g., due to interrupt
> > > > injection), hi++ will produce an incorrect result, silently corrupting
> > > > the counter value.
> > > >
> > > > Negligible benefit: hi changing between reads is an extremely rare
> > > > event. Optimizing away the retry loop for such a rare case provides no
> > > > meaningful performance gain. And if hi never stabilizes, that
> > > > indicates a hardware failure — in which case hanging in the retry loop
> > > > is actually the more appropriate behavior, as it makes the failure
> > > > visible rather than silently producing garbage values.
> > > >
> > > > The high-low-high do-while retry strategy exists precisely to handle
> > > > these cases correctly. I don't think this proposed sequence is a valid
> > > > replacement.
> > > >
> > >
> > > The point is that if the value is an incrementing counter (of some form)
> > > then the value is stale by the time the reading sequence completes.
> > > So all the code can be assumed to do is return a value the counter had
> > > sometime between when the code started and when it finished.
> > > If hi changes then hi+1:0 must have happened while the code was running
> > > so it is a safe return value.
> > You're right — (hi + 1):0 is enough for hi-lo-hi. Thank you for
> > sticking to your point; it helped me think more carefully and
> > understand the subtlety.
>
> Don't worry, I'll have written the loop in the past.
> Probably best is read old_hi-lo-hi then if (hi != old_hi) lo = 0
This is more conservative than (hi + 1):0.
--
Best Regards
Guo Ren
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-18 3:20 ` Vivian Wang
@ 2026-06-18 3:45 ` Guo Ren
-1 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-18 3:45 UTC (permalink / raw)
To: Vivian Wang
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On Thu, Jun 18, 2026 at 11:20 AM Vivian Wang <wangruikang@iscas.ac.cn> wrote:
>
> On 6/15/26 20:38, Guo Ren wrote:
> > Hi Zhanpeng Zhang,
> >
> > I noticed you posted a similar fix recently. However, I had already
> > submitted a similar solution back in September 2025 [1]. It would be
> > great if you could review it. A few concerns with the current patch:
> >
> > 1. Using spin_lock_irqsave in such a small semantic structure is
> > unnecessary. IRQ protection should be provided by the higher-level
> > IOMMU driver interfaces. Many existing call sites already handle IRQ
> > disabling, so adding it here feels redundant and adds avoidable
> > overhead.
> >
> > 2. More critically, the RISCV_IOMMU_32BIT_ACCESS Kconfig option is
> > problematic. According to the RISC-V IOMMU specification (Software
> > Guidelines, “Reading and writing IOMMU registers”):
> >
> >> Registers that are 64-bit wide may be accessed using either a 32-bit
> >> or a 64-bit access.
> > This clearly requires that any hardware supporting 64-bit MMIO access
> > must also support 32-bit MMIO access. Therefore, the IOMMU driver should
> > be built on a 32-bit access base for maximum hardware compatibility.
>
> I agree that the driver should be build with 32-bit accesses to support
> these hardware. However there's one minor thing that I want to propose
> regarding this, for the sake of clarity:
>
> RISC-V IOMMU implementations that do not support 64-bit accesses are not
> fully compliant with the RISC-V IOMMU specification and should not use
> the standard RISC-V identifiers, namely:
>
> - Devicetree compatible riscv,iommu
> - ACPI HID RSCV0004
>
> This, in my opinion, is not only the right thing to do (not claiming to
> be standard hardware without actually being standard hardware), but also
> saves a lot of trouble for operating system developers (yes, there are
> RISC-V operating systems other than Linux out there), since invalid IO
> accesses usually manifest as mysterious access faults (that would
> usually suggest an incorrect IOMMU address), or worse, system hangs.
>
> I would appreciate if the hardware vendors shipping 32-bit-access-only
> IOMMUs would consider this.
>
> Vivian "dramforever" Wang
>
32-bit MMIO access is explicitly supported by the RISC-V IOMMU
specification and is a valid choice (64-bit MMIO access is optional).
64-bit MMIO access is not "free" — it consumes additional interconnect
hardware resources.
Could you elaborate on the concrete benefit of using 64-bit MMIO
access in this driver, for these non-PMU registers? A clear
justification would help evaluate whether the trade-off is worthwhile.
--
Best Regards
Guo Ren
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-18 3:45 ` Guo Ren
0 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-18 3:45 UTC (permalink / raw)
To: Vivian Wang
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On Thu, Jun 18, 2026 at 11:20 AM Vivian Wang <wangruikang@iscas.ac.cn> wrote:
>
> On 6/15/26 20:38, Guo Ren wrote:
> > Hi Zhanpeng Zhang,
> >
> > I noticed you posted a similar fix recently. However, I had already
> > submitted a similar solution back in September 2025 [1]. It would be
> > great if you could review it. A few concerns with the current patch:
> >
> > 1. Using spin_lock_irqsave in such a small semantic structure is
> > unnecessary. IRQ protection should be provided by the higher-level
> > IOMMU driver interfaces. Many existing call sites already handle IRQ
> > disabling, so adding it here feels redundant and adds avoidable
> > overhead.
> >
> > 2. More critically, the RISCV_IOMMU_32BIT_ACCESS Kconfig option is
> > problematic. According to the RISC-V IOMMU specification (Software
> > Guidelines, “Reading and writing IOMMU registers”):
> >
> >> Registers that are 64-bit wide may be accessed using either a 32-bit
> >> or a 64-bit access.
> > This clearly requires that any hardware supporting 64-bit MMIO access
> > must also support 32-bit MMIO access. Therefore, the IOMMU driver should
> > be built on a 32-bit access base for maximum hardware compatibility.
>
> I agree that the driver should be build with 32-bit accesses to support
> these hardware. However there's one minor thing that I want to propose
> regarding this, for the sake of clarity:
>
> RISC-V IOMMU implementations that do not support 64-bit accesses are not
> fully compliant with the RISC-V IOMMU specification and should not use
> the standard RISC-V identifiers, namely:
>
> - Devicetree compatible riscv,iommu
> - ACPI HID RSCV0004
>
> This, in my opinion, is not only the right thing to do (not claiming to
> be standard hardware without actually being standard hardware), but also
> saves a lot of trouble for operating system developers (yes, there are
> RISC-V operating systems other than Linux out there), since invalid IO
> accesses usually manifest as mysterious access faults (that would
> usually suggest an incorrect IOMMU address), or worse, system hangs.
>
> I would appreciate if the hardware vendors shipping 32-bit-access-only
> IOMMUs would consider this.
>
> Vivian "dramforever" Wang
>
32-bit MMIO access is explicitly supported by the RISC-V IOMMU
specification and is a valid choice (64-bit MMIO access is optional).
64-bit MMIO access is not "free" — it consumes additional interconnect
hardware resources.
Could you elaborate on the concrete benefit of using 64-bit MMIO
access in this driver, for these non-PMU registers? A clear
justification would help evaluate whether the trade-off is worthwhile.
--
Best Regards
Guo Ren
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-18 3:45 ` Guo Ren
@ 2026-06-18 7:33 ` Vivian Wang
-1 siblings, 0 replies; 70+ messages in thread
From: Vivian Wang @ 2026-06-18 7:33 UTC (permalink / raw)
To: Guo Ren
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On 6/18/26 11:45, Guo Ren wrote:
> On Thu, Jun 18, 2026 at 11:20 AM Vivian Wang <wangruikang@iscas.ac.cn> wrote:
>> On 6/15/26 20:38, Guo Ren wrote:
>>> Hi Zhanpeng Zhang,
>>>
>>> I noticed you posted a similar fix recently. However, I had already
>>> submitted a similar solution back in September 2025 [1]. It would be
>>> great if you could review it. A few concerns with the current patch:
>>>
>>> 1. Using spin_lock_irqsave in such a small semantic structure is
>>> unnecessary. IRQ protection should be provided by the higher-level
>>> IOMMU driver interfaces. Many existing call sites already handle IRQ
>>> disabling, so adding it here feels redundant and adds avoidable
>>> overhead.
>>>
>>> 2. More critically, the RISCV_IOMMU_32BIT_ACCESS Kconfig option is
>>> problematic. According to the RISC-V IOMMU specification (Software
>>> Guidelines, “Reading and writing IOMMU registers”):
>>>
>>>> Registers that are 64-bit wide may be accessed using either a 32-bit
>>>> or a 64-bit access.
>>> This clearly requires that any hardware supporting 64-bit MMIO access
>>> must also support 32-bit MMIO access. Therefore, the IOMMU driver should
>>> be built on a 32-bit access base for maximum hardware compatibility.
>> I agree that the driver should be build with 32-bit accesses to support
>> these hardware. However there's one minor thing that I want to propose
>> regarding this, for the sake of clarity:
>>
>> RISC-V IOMMU implementations that do not support 64-bit accesses are not
>> fully compliant with the RISC-V IOMMU specification and should not use
>> the standard RISC-V identifiers, namely:
>>
>> - Devicetree compatible riscv,iommu
>> - ACPI HID RSCV0004
>>
>> This, in my opinion, is not only the right thing to do (not claiming to
>> be standard hardware without actually being standard hardware), but also
>> saves a lot of trouble for operating system developers (yes, there are
>> RISC-V operating systems other than Linux out there), since invalid IO
>> accesses usually manifest as mysterious access faults (that would
>> usually suggest an incorrect IOMMU address), or worse, system hangs.
>>
>> I would appreciate if the hardware vendors shipping 32-bit-access-only
>> IOMMUs would consider this.
>>
>> Vivian "dramforever" Wang
>>
> 32-bit MMIO access is explicitly supported by the RISC-V IOMMU
> specification and is a valid choice (64-bit MMIO access is optional).
Hmm... I think it's optional in software but not optional in hardware,
but the specs are not super clear on this. I've opened a GitHub issue to
ask:
https://github.com/riscv-non-isa/riscv-iommu/issues/765
> 64-bit MMIO access is not "free" — it consumes additional interconnect
> hardware resources.
>
> Could you elaborate on the concrete benefit of using 64-bit MMIO
> access in this driver, for these non-PMU registers? A clear
> justification would help evaluate whether the trade-off is worthwhile.
>
>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-18 7:33 ` Vivian Wang
0 siblings, 0 replies; 70+ messages in thread
From: Vivian Wang @ 2026-06-18 7:33 UTC (permalink / raw)
To: Guo Ren
Cc: zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu, joro,
linux-kernel, linux-riscv, luxu.kernel, palmer, pjw, robin.murphy,
tjeznach, will, yuanzhu
On 6/18/26 11:45, Guo Ren wrote:
> On Thu, Jun 18, 2026 at 11:20 AM Vivian Wang <wangruikang@iscas.ac.cn> wrote:
>> On 6/15/26 20:38, Guo Ren wrote:
>>> Hi Zhanpeng Zhang,
>>>
>>> I noticed you posted a similar fix recently. However, I had already
>>> submitted a similar solution back in September 2025 [1]. It would be
>>> great if you could review it. A few concerns with the current patch:
>>>
>>> 1. Using spin_lock_irqsave in such a small semantic structure is
>>> unnecessary. IRQ protection should be provided by the higher-level
>>> IOMMU driver interfaces. Many existing call sites already handle IRQ
>>> disabling, so adding it here feels redundant and adds avoidable
>>> overhead.
>>>
>>> 2. More critically, the RISCV_IOMMU_32BIT_ACCESS Kconfig option is
>>> problematic. According to the RISC-V IOMMU specification (Software
>>> Guidelines, “Reading and writing IOMMU registers”):
>>>
>>>> Registers that are 64-bit wide may be accessed using either a 32-bit
>>>> or a 64-bit access.
>>> This clearly requires that any hardware supporting 64-bit MMIO access
>>> must also support 32-bit MMIO access. Therefore, the IOMMU driver should
>>> be built on a 32-bit access base for maximum hardware compatibility.
>> I agree that the driver should be build with 32-bit accesses to support
>> these hardware. However there's one minor thing that I want to propose
>> regarding this, for the sake of clarity:
>>
>> RISC-V IOMMU implementations that do not support 64-bit accesses are not
>> fully compliant with the RISC-V IOMMU specification and should not use
>> the standard RISC-V identifiers, namely:
>>
>> - Devicetree compatible riscv,iommu
>> - ACPI HID RSCV0004
>>
>> This, in my opinion, is not only the right thing to do (not claiming to
>> be standard hardware without actually being standard hardware), but also
>> saves a lot of trouble for operating system developers (yes, there are
>> RISC-V operating systems other than Linux out there), since invalid IO
>> accesses usually manifest as mysterious access faults (that would
>> usually suggest an incorrect IOMMU address), or worse, system hangs.
>>
>> I would appreciate if the hardware vendors shipping 32-bit-access-only
>> IOMMUs would consider this.
>>
>> Vivian "dramforever" Wang
>>
> 32-bit MMIO access is explicitly supported by the RISC-V IOMMU
> specification and is a valid choice (64-bit MMIO access is optional).
Hmm... I think it's optional in software but not optional in hardware,
but the specs are not super clear on this. I've opened a GitHub issue to
ask:
https://github.com/riscv-non-isa/riscv-iommu/issues/765
> 64-bit MMIO access is not "free" — it consumes additional interconnect
> hardware resources.
>
> Could you elaborate on the concrete benefit of using 64-bit MMIO
> access in this driver, for these non-PMU registers? A clear
> justification would help evaluate whether the trade-off is worthwhile.
>
>
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-18 7:33 ` Vivian Wang
@ 2026-06-18 9:51 ` Guo Ren
-1 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-18 9:51 UTC (permalink / raw)
To: Vivian Wang, zhangzhanpeng.jasper
Cc: alex, aou, cuiyunhui, iommu, joro, linux-kernel, linux-riscv,
luxu.kernel, palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
Hi Vivian,
As noted in the RISC-V IOMMU Specification, Chapter 6:
> Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
not clearly defined in the current ratified RISC-V IOMMU
specification. To handle this correctly, the Linux RISC-V IOMMU driver
should fall back to 32-bit MMIO accesses when reading 64-bit registers
(e.g., performance counters). The behavior of 32-bit MMIO accesses is
more precisely defined in the RISC-V IOMMU specification.
Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
MMIO) based on the current ratified RISC-V IOMMU specification, and
this driver does not appear to benefit from 64-bit MMIO access either.
Performance is fundamentally constrained by bus latency; assuming that
simply reducing the number of accesses will improve performance is an
oversimplification that ignores the underlying hardware
characteristics.
Hi Zhang,
The hardware targeted by the 32-bit MMIO access in your patch is
precisely the kind that deserves the official ACPI HID RSCV0004 and
Devicetree compatible string (riscv,iommu). It is rather the hardware
that supports only 64-bit MMIO access — whose atomicity is explicitly
left as UNSPECIFIED by the RISC-V IOMMU specification — that should be
relegated to a non-standard ACPI ID. That is what truly constitutes
poor hardware design.
I will consistently support your position of adopting 32-bit MMIO
access in this patch, and will advocate for making it the default
behavior in the Linux RISC-V IOMMU driver, as it better aligns with
the intent of the RISC-V IOMMU specification.
--
Best Regards
Guo Ren
On Thu, Jun 18, 2026 at 3:33 PM Vivian Wang <wangruikang@iscas.ac.cn> wrote:
>
> On 6/18/26 11:45, Guo Ren wrote:
> > On Thu, Jun 18, 2026 at 11:20 AM Vivian Wang <wangruikang@iscas.ac.cn> wrote:
> >> On 6/15/26 20:38, Guo Ren wrote:
> >>> Hi Zhanpeng Zhang,
> >>>
> >>> I noticed you posted a similar fix recently. However, I had already
> >>> submitted a similar solution back in September 2025 [1]. It would be
> >>> great if you could review it. A few concerns with the current patch:
> >>>
> >>> 1. Using spin_lock_irqsave in such a small semantic structure is
> >>> unnecessary. IRQ protection should be provided by the higher-level
> >>> IOMMU driver interfaces. Many existing call sites already handle IRQ
> >>> disabling, so adding it here feels redundant and adds avoidable
> >>> overhead.
> >>>
> >>> 2. More critically, the RISCV_IOMMU_32BIT_ACCESS Kconfig option is
> >>> problematic. According to the RISC-V IOMMU specification (Software
> >>> Guidelines, “Reading and writing IOMMU registers”):
> >>>
> >>>> Registers that are 64-bit wide may be accessed using either a 32-bit
> >>>> or a 64-bit access.
> >>> This clearly requires that any hardware supporting 64-bit MMIO access
> >>> must also support 32-bit MMIO access. Therefore, the IOMMU driver should
> >>> be built on a 32-bit access base for maximum hardware compatibility.
> >> I agree that the driver should be build with 32-bit accesses to support
> >> these hardware. However there's one minor thing that I want to propose
> >> regarding this, for the sake of clarity:
> >>
> >> RISC-V IOMMU implementations that do not support 64-bit accesses are not
> >> fully compliant with the RISC-V IOMMU specification and should not use
> >> the standard RISC-V identifiers, namely:
> >>
> >> - Devicetree compatible riscv,iommu
> >> - ACPI HID RSCV0004
> >>
> >> This, in my opinion, is not only the right thing to do (not claiming to
> >> be standard hardware without actually being standard hardware), but also
> >> saves a lot of trouble for operating system developers (yes, there are
> >> RISC-V operating systems other than Linux out there), since invalid IO
> >> accesses usually manifest as mysterious access faults (that would
> >> usually suggest an incorrect IOMMU address), or worse, system hangs.
> >>
> >> I would appreciate if the hardware vendors shipping 32-bit-access-only
> >> IOMMUs would consider this.
> >>
> >> Vivian "dramforever" Wang
> >>
> > 32-bit MMIO access is explicitly supported by the RISC-V IOMMU
> > specification and is a valid choice (64-bit MMIO access is optional).
>
> Hmm... I think it's optional in software but not optional in hardware,
> but the specs are not super clear on this. I've opened a GitHub issue to
> ask:
>
> https://github.com/riscv-non-isa/riscv-iommu/issues/765
>
> > 64-bit MMIO access is not "free" — it consumes additional interconnect
> > hardware resources.
> >
> > Could you elaborate on the concrete benefit of using 64-bit MMIO
> > access in this driver, for these non-PMU registers? A clear
> > justification would help evaluate whether the trade-off is worthwhile.
> >
> >
>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-18 9:51 ` Guo Ren
0 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-18 9:51 UTC (permalink / raw)
To: Vivian Wang, zhangzhanpeng.jasper
Cc: alex, aou, cuiyunhui, iommu, joro, linux-kernel, linux-riscv,
luxu.kernel, palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
Hi Vivian,
As noted in the RISC-V IOMMU Specification, Chapter 6:
> Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
not clearly defined in the current ratified RISC-V IOMMU
specification. To handle this correctly, the Linux RISC-V IOMMU driver
should fall back to 32-bit MMIO accesses when reading 64-bit registers
(e.g., performance counters). The behavior of 32-bit MMIO accesses is
more precisely defined in the RISC-V IOMMU specification.
Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
MMIO) based on the current ratified RISC-V IOMMU specification, and
this driver does not appear to benefit from 64-bit MMIO access either.
Performance is fundamentally constrained by bus latency; assuming that
simply reducing the number of accesses will improve performance is an
oversimplification that ignores the underlying hardware
characteristics.
Hi Zhang,
The hardware targeted by the 32-bit MMIO access in your patch is
precisely the kind that deserves the official ACPI HID RSCV0004 and
Devicetree compatible string (riscv,iommu). It is rather the hardware
that supports only 64-bit MMIO access — whose atomicity is explicitly
left as UNSPECIFIED by the RISC-V IOMMU specification — that should be
relegated to a non-standard ACPI ID. That is what truly constitutes
poor hardware design.
I will consistently support your position of adopting 32-bit MMIO
access in this patch, and will advocate for making it the default
behavior in the Linux RISC-V IOMMU driver, as it better aligns with
the intent of the RISC-V IOMMU specification.
--
Best Regards
Guo Ren
On Thu, Jun 18, 2026 at 3:33 PM Vivian Wang <wangruikang@iscas.ac.cn> wrote:
>
> On 6/18/26 11:45, Guo Ren wrote:
> > On Thu, Jun 18, 2026 at 11:20 AM Vivian Wang <wangruikang@iscas.ac.cn> wrote:
> >> On 6/15/26 20:38, Guo Ren wrote:
> >>> Hi Zhanpeng Zhang,
> >>>
> >>> I noticed you posted a similar fix recently. However, I had already
> >>> submitted a similar solution back in September 2025 [1]. It would be
> >>> great if you could review it. A few concerns with the current patch:
> >>>
> >>> 1. Using spin_lock_irqsave in such a small semantic structure is
> >>> unnecessary. IRQ protection should be provided by the higher-level
> >>> IOMMU driver interfaces. Many existing call sites already handle IRQ
> >>> disabling, so adding it here feels redundant and adds avoidable
> >>> overhead.
> >>>
> >>> 2. More critically, the RISCV_IOMMU_32BIT_ACCESS Kconfig option is
> >>> problematic. According to the RISC-V IOMMU specification (Software
> >>> Guidelines, “Reading and writing IOMMU registers”):
> >>>
> >>>> Registers that are 64-bit wide may be accessed using either a 32-bit
> >>>> or a 64-bit access.
> >>> This clearly requires that any hardware supporting 64-bit MMIO access
> >>> must also support 32-bit MMIO access. Therefore, the IOMMU driver should
> >>> be built on a 32-bit access base for maximum hardware compatibility.
> >> I agree that the driver should be build with 32-bit accesses to support
> >> these hardware. However there's one minor thing that I want to propose
> >> regarding this, for the sake of clarity:
> >>
> >> RISC-V IOMMU implementations that do not support 64-bit accesses are not
> >> fully compliant with the RISC-V IOMMU specification and should not use
> >> the standard RISC-V identifiers, namely:
> >>
> >> - Devicetree compatible riscv,iommu
> >> - ACPI HID RSCV0004
> >>
> >> This, in my opinion, is not only the right thing to do (not claiming to
> >> be standard hardware without actually being standard hardware), but also
> >> saves a lot of trouble for operating system developers (yes, there are
> >> RISC-V operating systems other than Linux out there), since invalid IO
> >> accesses usually manifest as mysterious access faults (that would
> >> usually suggest an incorrect IOMMU address), or worse, system hangs.
> >>
> >> I would appreciate if the hardware vendors shipping 32-bit-access-only
> >> IOMMUs would consider this.
> >>
> >> Vivian "dramforever" Wang
> >>
> > 32-bit MMIO access is explicitly supported by the RISC-V IOMMU
> > specification and is a valid choice (64-bit MMIO access is optional).
>
> Hmm... I think it's optional in software but not optional in hardware,
> but the specs are not super clear on this. I've opened a GitHub issue to
> ask:
>
> https://github.com/riscv-non-isa/riscv-iommu/issues/765
>
> > 64-bit MMIO access is not "free" — it consumes additional interconnect
> > hardware resources.
> >
> > Could you elaborate on the concrete benefit of using 64-bit MMIO
> > access in this driver, for these non-PMU registers? A clear
> > justification would help evaluate whether the trade-off is worthwhile.
> >
> >
>
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-18 9:51 ` Guo Ren
@ 2026-06-18 10:01 ` Vivian Wang
-1 siblings, 0 replies; 70+ messages in thread
From: Vivian Wang @ 2026-06-18 10:01 UTC (permalink / raw)
To: Guo Ren, zhangzhanpeng.jasper
Cc: alex, aou, cuiyunhui, iommu, joro, linux-kernel, linux-riscv,
luxu.kernel, palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
On 6/18/26 17:51, Guo Ren wrote:
> Hi Vivian,
>
> As noted in the RISC-V IOMMU Specification, Chapter 6:
>> Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
> Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
> not clearly defined in the current ratified RISC-V IOMMU
> specification. To handle this correctly, the Linux RISC-V IOMMU driver
> should fall back to 32-bit MMIO accesses when reading 64-bit registers
> (e.g., performance counters). The behavior of 32-bit MMIO accesses is
> more precisely defined in the RISC-V IOMMU specification.
>
> Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
> MMIO) based on the current ratified RISC-V IOMMU specification, and
> this driver does not appear to benefit from 64-bit MMIO access either.
> Performance is fundamentally constrained by bus latency; assuming that
> simply reducing the number of accesses will improve performance is an
> oversimplification that ignores the underlying hardware
> characteristics.
Just as a clarification, I had never said that the driver *should* use
64-bit accesses. In fact, I said:
I agree that the driver should be build with 32-bit accesses to support
these hardware.
Perhaps I wasn't clear enough. I meant that I agree that the RISC-V
IOMMU driver should use 32-bit accesses. At *no* point had I claimed
that 64-bit accesses are better.
> Hi Zhang,
>
> The hardware targeted by the 32-bit MMIO access in your patch is
> precisely the kind that deserves the official ACPI HID RSCV0004 and
> Devicetree compatible string (riscv,iommu). It is rather the hardware
> that supports only 64-bit MMIO access — whose atomicity is explicitly
> [...]
IOMMU Hardware that supports only 64-bit MMIO access for 8-byte
registers has never come up in this discussion. If they ever show up
we'll talk about them then.
Vivian "dramforever" Wang
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-18 10:01 ` Vivian Wang
0 siblings, 0 replies; 70+ messages in thread
From: Vivian Wang @ 2026-06-18 10:01 UTC (permalink / raw)
To: Guo Ren, zhangzhanpeng.jasper
Cc: alex, aou, cuiyunhui, iommu, joro, linux-kernel, linux-riscv,
luxu.kernel, palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
On 6/18/26 17:51, Guo Ren wrote:
> Hi Vivian,
>
> As noted in the RISC-V IOMMU Specification, Chapter 6:
>> Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
> Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
> not clearly defined in the current ratified RISC-V IOMMU
> specification. To handle this correctly, the Linux RISC-V IOMMU driver
> should fall back to 32-bit MMIO accesses when reading 64-bit registers
> (e.g., performance counters). The behavior of 32-bit MMIO accesses is
> more precisely defined in the RISC-V IOMMU specification.
>
> Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
> MMIO) based on the current ratified RISC-V IOMMU specification, and
> this driver does not appear to benefit from 64-bit MMIO access either.
> Performance is fundamentally constrained by bus latency; assuming that
> simply reducing the number of accesses will improve performance is an
> oversimplification that ignores the underlying hardware
> characteristics.
Just as a clarification, I had never said that the driver *should* use
64-bit accesses. In fact, I said:
I agree that the driver should be build with 32-bit accesses to support
these hardware.
Perhaps I wasn't clear enough. I meant that I agree that the RISC-V
IOMMU driver should use 32-bit accesses. At *no* point had I claimed
that 64-bit accesses are better.
> Hi Zhang,
>
> The hardware targeted by the 32-bit MMIO access in your patch is
> precisely the kind that deserves the official ACPI HID RSCV0004 and
> Devicetree compatible string (riscv,iommu). It is rather the hardware
> that supports only 64-bit MMIO access — whose atomicity is explicitly
> [...]
IOMMU Hardware that supports only 64-bit MMIO access for 8-byte
registers has never come up in this discussion. If they ever show up
we'll talk about them then.
Vivian "dramforever" Wang
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-18 9:51 ` Guo Ren
@ 2026-06-18 13:36 ` David Laight
-1 siblings, 0 replies; 70+ messages in thread
From: David Laight @ 2026-06-18 13:36 UTC (permalink / raw)
To: Guo Ren
Cc: Vivian Wang, zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu,
joro, linux-kernel, linux-riscv, luxu.kernel, palmer, pjw,
robin.murphy, tjeznach, will, yuanzhu
On Thu, 18 Jun 2026 17:51:34 +0800
Guo Ren <guoren@kernel.org> wrote:
> Hi Vivian,
>
> As noted in the RISC-V IOMMU Specification, Chapter 6:
> > Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
>
> Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
> not clearly defined in the current ratified RISC-V IOMMU
> specification. To handle this correctly, the Linux RISC-V IOMMU driver
> should fall back to 32-bit MMIO accesses when reading 64-bit registers
> (e.g., performance counters). The behavior of 32-bit MMIO accesses is
> more precisely defined in the RISC-V IOMMU specification.
>
> Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
> MMIO) based on the current ratified RISC-V IOMMU specification, and
> this driver does not appear to benefit from 64-bit MMIO access either.
> Performance is fundamentally constrained by bus latency; assuming that
> simply reducing the number of accesses will improve performance is an
> oversimplification that ignores the underlying hardware
> characteristics.
If the bus latency is significant it is almost certainly worth using
memory accesses to avoid re-reading the hi register.
Something like this might work:
static volatile u32 hi_prev, lo_prev;
u32 hi = read_reg_hi();
u32 lo = read_reg_lo();
if (lo <= lo_prev || hi != hi_prev) {
u32 hi_tmp = read_reg_hi;
if (hi_tmp != hi) {
hi = hi_tmp;
lo = 0;
}
lo_prev = ~0u;
hi_prev = hi;
}
lo_prev = lo;
return (u64)hi << 32 | lo;
It shouldn't need any locking but the accesses do need to be ordered.
David
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-18 13:36 ` David Laight
0 siblings, 0 replies; 70+ messages in thread
From: David Laight @ 2026-06-18 13:36 UTC (permalink / raw)
To: Guo Ren
Cc: Vivian Wang, zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu,
joro, linux-kernel, linux-riscv, luxu.kernel, palmer, pjw,
robin.murphy, tjeznach, will, yuanzhu
On Thu, 18 Jun 2026 17:51:34 +0800
Guo Ren <guoren@kernel.org> wrote:
> Hi Vivian,
>
> As noted in the RISC-V IOMMU Specification, Chapter 6:
> > Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
>
> Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
> not clearly defined in the current ratified RISC-V IOMMU
> specification. To handle this correctly, the Linux RISC-V IOMMU driver
> should fall back to 32-bit MMIO accesses when reading 64-bit registers
> (e.g., performance counters). The behavior of 32-bit MMIO accesses is
> more precisely defined in the RISC-V IOMMU specification.
>
> Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
> MMIO) based on the current ratified RISC-V IOMMU specification, and
> this driver does not appear to benefit from 64-bit MMIO access either.
> Performance is fundamentally constrained by bus latency; assuming that
> simply reducing the number of accesses will improve performance is an
> oversimplification that ignores the underlying hardware
> characteristics.
If the bus latency is significant it is almost certainly worth using
memory accesses to avoid re-reading the hi register.
Something like this might work:
static volatile u32 hi_prev, lo_prev;
u32 hi = read_reg_hi();
u32 lo = read_reg_lo();
if (lo <= lo_prev || hi != hi_prev) {
u32 hi_tmp = read_reg_hi;
if (hi_tmp != hi) {
hi = hi_tmp;
lo = 0;
}
lo_prev = ~0u;
hi_prev = hi;
}
lo_prev = lo;
return (u64)hi << 32 | lo;
It shouldn't need any locking but the accesses do need to be ordered.
David
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-18 13:36 ` David Laight
@ 2026-06-18 16:40 ` Guo Ren
-1 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-18 16:40 UTC (permalink / raw)
To: David Laight
Cc: Vivian Wang, zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu,
joro, linux-kernel, linux-riscv, luxu.kernel, palmer, pjw,
robin.murphy, tjeznach, will, yuanzhu
On Thu, Jun 18, 2026 at 9:36 PM David Laight
<david.laight.linux@gmail.com> wrote:
>
> On Thu, 18 Jun 2026 17:51:34 +0800
> Guo Ren <guoren@kernel.org> wrote:
>
> > Hi Vivian,
> >
> > As noted in the RISC-V IOMMU Specification, Chapter 6:
> > > Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
> >
> > Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
> > not clearly defined in the current ratified RISC-V IOMMU
> > specification. To handle this correctly, the Linux RISC-V IOMMU driver
> > should fall back to 32-bit MMIO accesses when reading 64-bit registers
> > (e.g., performance counters). The behavior of 32-bit MMIO accesses is
> > more precisely defined in the RISC-V IOMMU specification.
> >
> > Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
> > MMIO) based on the current ratified RISC-V IOMMU specification, and
> > this driver does not appear to benefit from 64-bit MMIO access either.
> > Performance is fundamentally constrained by bus latency; assuming that
> > simply reducing the number of accesses will improve performance is an
> > oversimplification that ignores the underlying hardware
> > characteristics.
>
> If the bus latency is significant it is almost certainly worth using
> memory accesses to avoid re-reading the hi register.
>
> Something like this might work:
>
> static volatile u32 hi_prev, lo_prev;
>
> u32 hi = read_reg_hi();
> u32 lo = read_reg_lo();
>
> if (lo <= lo_prev || hi != hi_prev) {
> u32 hi_tmp = read_reg_hi;
> if (hi_tmp != hi) {
> hi = hi_tmp;
> lo = 0;
> }
> lo_prev = ~0u;
> hi_prev = hi;
> }
> lo_prev = lo;
> return (u64)hi << 32 | lo;
>
> It shouldn't need any locking but the accesses do need to be ordered.
Thank you for the suggestion. However, I believe this feedback is more
relevant to the RISC-V IOMMU HPM patchset [1], as no counter registers
are involved in the current patchset. That said, the idea of improving
the hi-lo-hi slow-path mechanism to better handle high-latency
hardware scenarios is well taken and worth discussing in the
appropriate thread.
[1]: https://lore.kernel.org/linux-riscv/20260208063848.3547817-2-zong.li@sifive.com/
P.S. The hardware I have at hand exhibits very low interconnect
latency. And I have never observed the slow path where hi_tmp != hi
being triggered — my approach was to remove the retry mechanism
directly in 32-bit mmio mode and run stress tests to check whether
perf stat produced incorrect results. That said, I may have simply
been lucky instead of hw guarantee.
--
Best Regards
Guo Ren
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-18 16:40 ` Guo Ren
0 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-18 16:40 UTC (permalink / raw)
To: David Laight
Cc: Vivian Wang, zhangzhanpeng.jasper, alex, aou, cuiyunhui, iommu,
joro, linux-kernel, linux-riscv, luxu.kernel, palmer, pjw,
robin.murphy, tjeznach, will, yuanzhu
On Thu, Jun 18, 2026 at 9:36 PM David Laight
<david.laight.linux@gmail.com> wrote:
>
> On Thu, 18 Jun 2026 17:51:34 +0800
> Guo Ren <guoren@kernel.org> wrote:
>
> > Hi Vivian,
> >
> > As noted in the RISC-V IOMMU Specification, Chapter 6:
> > > Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
> >
> > Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
> > not clearly defined in the current ratified RISC-V IOMMU
> > specification. To handle this correctly, the Linux RISC-V IOMMU driver
> > should fall back to 32-bit MMIO accesses when reading 64-bit registers
> > (e.g., performance counters). The behavior of 32-bit MMIO accesses is
> > more precisely defined in the RISC-V IOMMU specification.
> >
> > Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
> > MMIO) based on the current ratified RISC-V IOMMU specification, and
> > this driver does not appear to benefit from 64-bit MMIO access either.
> > Performance is fundamentally constrained by bus latency; assuming that
> > simply reducing the number of accesses will improve performance is an
> > oversimplification that ignores the underlying hardware
> > characteristics.
>
> If the bus latency is significant it is almost certainly worth using
> memory accesses to avoid re-reading the hi register.
>
> Something like this might work:
>
> static volatile u32 hi_prev, lo_prev;
>
> u32 hi = read_reg_hi();
> u32 lo = read_reg_lo();
>
> if (lo <= lo_prev || hi != hi_prev) {
> u32 hi_tmp = read_reg_hi;
> if (hi_tmp != hi) {
> hi = hi_tmp;
> lo = 0;
> }
> lo_prev = ~0u;
> hi_prev = hi;
> }
> lo_prev = lo;
> return (u64)hi << 32 | lo;
>
> It shouldn't need any locking but the accesses do need to be ordered.
Thank you for the suggestion. However, I believe this feedback is more
relevant to the RISC-V IOMMU HPM patchset [1], as no counter registers
are involved in the current patchset. That said, the idea of improving
the hi-lo-hi slow-path mechanism to better handle high-latency
hardware scenarios is well taken and worth discussing in the
appropriate thread.
[1]: https://lore.kernel.org/linux-riscv/20260208063848.3547817-2-zong.li@sifive.com/
P.S. The hardware I have at hand exhibits very low interconnect
latency. And I have never observed the slow path where hi_tmp != hi
being triggered — my approach was to remove the retry mechanism
directly in 32-bit mmio mode and run stress tests to check whether
perf stat produced incorrect results. That said, I may have simply
been lucky instead of hw guarantee.
--
Best Regards
Guo Ren
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-18 16:40 ` Guo Ren
@ 2026-06-23 9:20 ` Zong Li
-1 siblings, 0 replies; 70+ messages in thread
From: Zong Li @ 2026-06-23 9:20 UTC (permalink / raw)
To: Guo Ren
Cc: David Laight, Vivian Wang, zhangzhanpeng.jasper, alex, aou,
cuiyunhui, iommu, joro, linux-kernel, linux-riscv, luxu.kernel,
palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
On Fri, Jun 19, 2026 at 1:14 AM Guo Ren <guoren@kernel.org> wrote:
>
> On Thu, Jun 18, 2026 at 9:36 PM David Laight
> <david.laight.linux@gmail.com> wrote:
> >
> > On Thu, 18 Jun 2026 17:51:34 +0800
> > Guo Ren <guoren@kernel.org> wrote:
> >
> > > Hi Vivian,
> > >
> > > As noted in the RISC-V IOMMU Specification, Chapter 6:
> > > > Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
> > >
> > > Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
> > > not clearly defined in the current ratified RISC-V IOMMU
> > > specification. To handle this correctly, the Linux RISC-V IOMMU driver
> > > should fall back to 32-bit MMIO accesses when reading 64-bit registers
> > > (e.g., performance counters). The behavior of 32-bit MMIO accesses is
> > > more precisely defined in the RISC-V IOMMU specification.
> > >
> > > Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
> > > MMIO) based on the current ratified RISC-V IOMMU specification, and
> > > this driver does not appear to benefit from 64-bit MMIO access either.
> > > Performance is fundamentally constrained by bus latency; assuming that
> > > simply reducing the number of accesses will improve performance is an
> > > oversimplification that ignores the underlying hardware
> > > characteristics.
> >
> > If the bus latency is significant it is almost certainly worth using
> > memory accesses to avoid re-reading the hi register.
> >
> > Something like this might work:
> >
> > static volatile u32 hi_prev, lo_prev;
> >
> > u32 hi = read_reg_hi();
> > u32 lo = read_reg_lo();
> >
> > if (lo <= lo_prev || hi != hi_prev) {
> > u32 hi_tmp = read_reg_hi;
> > if (hi_tmp != hi) {
> > hi = hi_tmp;
> > lo = 0;
> > }
> > lo_prev = ~0u;
> > hi_prev = hi;
> > }
> > lo_prev = lo;
> > return (u64)hi << 32 | lo;
> >
> > It shouldn't need any locking but the accesses do need to be ordered.
> Thank you for the suggestion. However, I believe this feedback is more
> relevant to the RISC-V IOMMU HPM patchset [1], as no counter registers
> are involved in the current patchset. That said, the idea of improving
> the hi-lo-hi slow-path mechanism to better handle high-latency
> hardware scenarios is well taken and worth discussing in the
> appropriate thread.
> [1]: https://lore.kernel.org/linux-riscv/20260208063848.3547817-2-zong.li@sifive.com/
>
> P.S. The hardware I have at hand exhibits very low interconnect
> latency. And I have never observed the slow path where hi_tmp != hi
> being triggered — my approach was to remove the retry mechanism
> directly in 32-bit mmio mode and run stress tests to check whether
> perf stat produced incorrect results. That said, I may have simply
> been lucky instead of hw guarantee.
>
Hi everyone,
Thank you for adding me to this discussion. I took some time to read
the previous messages.
Regarding the GitHub issue mentioned by Vivian, I noticed someone
pointed out that the hardware must support 64-bit access.
https://github.com/riscv-non-isa/riscv-iommu/issues/765#issuecomment-4742941894
I would like to confirm if that is correct. If so, should we update
the spec? And does this mean we do not need to modify the driver?
Thanks
> --
> Best Regards
> Guo Ren
>
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-23 9:20 ` Zong Li
0 siblings, 0 replies; 70+ messages in thread
From: Zong Li @ 2026-06-23 9:20 UTC (permalink / raw)
To: Guo Ren
Cc: David Laight, Vivian Wang, zhangzhanpeng.jasper, alex, aou,
cuiyunhui, iommu, joro, linux-kernel, linux-riscv, luxu.kernel,
palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
On Fri, Jun 19, 2026 at 1:14 AM Guo Ren <guoren@kernel.org> wrote:
>
> On Thu, Jun 18, 2026 at 9:36 PM David Laight
> <david.laight.linux@gmail.com> wrote:
> >
> > On Thu, 18 Jun 2026 17:51:34 +0800
> > Guo Ren <guoren@kernel.org> wrote:
> >
> > > Hi Vivian,
> > >
> > > As noted in the RISC-V IOMMU Specification, Chapter 6:
> > > > Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
> > >
> > > Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
> > > not clearly defined in the current ratified RISC-V IOMMU
> > > specification. To handle this correctly, the Linux RISC-V IOMMU driver
> > > should fall back to 32-bit MMIO accesses when reading 64-bit registers
> > > (e.g., performance counters). The behavior of 32-bit MMIO accesses is
> > > more precisely defined in the RISC-V IOMMU specification.
> > >
> > > Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
> > > MMIO) based on the current ratified RISC-V IOMMU specification, and
> > > this driver does not appear to benefit from 64-bit MMIO access either.
> > > Performance is fundamentally constrained by bus latency; assuming that
> > > simply reducing the number of accesses will improve performance is an
> > > oversimplification that ignores the underlying hardware
> > > characteristics.
> >
> > If the bus latency is significant it is almost certainly worth using
> > memory accesses to avoid re-reading the hi register.
> >
> > Something like this might work:
> >
> > static volatile u32 hi_prev, lo_prev;
> >
> > u32 hi = read_reg_hi();
> > u32 lo = read_reg_lo();
> >
> > if (lo <= lo_prev || hi != hi_prev) {
> > u32 hi_tmp = read_reg_hi;
> > if (hi_tmp != hi) {
> > hi = hi_tmp;
> > lo = 0;
> > }
> > lo_prev = ~0u;
> > hi_prev = hi;
> > }
> > lo_prev = lo;
> > return (u64)hi << 32 | lo;
> >
> > It shouldn't need any locking but the accesses do need to be ordered.
> Thank you for the suggestion. However, I believe this feedback is more
> relevant to the RISC-V IOMMU HPM patchset [1], as no counter registers
> are involved in the current patchset. That said, the idea of improving
> the hi-lo-hi slow-path mechanism to better handle high-latency
> hardware scenarios is well taken and worth discussing in the
> appropriate thread.
> [1]: https://lore.kernel.org/linux-riscv/20260208063848.3547817-2-zong.li@sifive.com/
>
> P.S. The hardware I have at hand exhibits very low interconnect
> latency. And I have never observed the slow path where hi_tmp != hi
> being triggered — my approach was to remove the retry mechanism
> directly in 32-bit mmio mode and run stress tests to check whether
> perf stat produced incorrect results. That said, I may have simply
> been lucky instead of hw guarantee.
>
Hi everyone,
Thank you for adding me to this discussion. I took some time to read
the previous messages.
Regarding the GitHub issue mentioned by Vivian, I noticed someone
pointed out that the hardware must support 64-bit access.
https://github.com/riscv-non-isa/riscv-iommu/issues/765#issuecomment-4742941894
I would like to confirm if that is correct. If so, should we update
the spec? And does this mean we do not need to modify the driver?
Thanks
> --
> Best Regards
> Guo Ren
>
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-18 16:40 ` Guo Ren
@ 2026-06-26 9:18 ` Zhanpeng Zhang
-1 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-06-26 9:18 UTC (permalink / raw)
To: Guo Ren, Vivian Wang
Cc: David Laight, alex, aou, cuiyunhui, iommu, joro, linux-kernel,
linux-riscv, luxu.kernel, palmer, pjw, robin.murphy, tjeznach,
will, yuanzhu, zong.li@sifive.com
Hi Vivian, Guo Ren,
Thanks for the discussion. I just caught up with the thread and the
GitHub issue [1].
I think we probably do not need to spend too much time discussing the exact
meaning of RSCV0004 around 32-bit vs 64-bit MMIO access. At least from my
side, I do not see much practical benefit in splitting the standard HID for
this distinction.
The original problem I want to solve is simple: Linux should have a compatible
and maintainable way to support IOMMUs that need 32-bit MMIO accesses for
64-bit registers, instead of taking an access fault that leads to panic.
Ideally, one generic kernel should support both access sequences without a
build-time option.
Would it be reasonable to keep RSCV0004 / riscv,iommu as the common device
identifier, and describe the required register access width as a runtime
firmware property instead? For ACPI, this might be an _DSD property on the
IOMMU device; for devicetree, it could be a matching DT property. The driver
could then check it during enumeration and select the appropriate register
accessors.
This avoids redefining the ACPI HID or compatible string, keeps one generic
kernel image, and makes the 32-bit MMIO fallback explicit in firmware. My goal
is just to implement the spec wording that "Registers that are 64-bit wide may
be accessed using either a 32-bit or a 64-bit access." in a lightweight way
that is acceptable for the Linux driver.
[1] https://github.com/riscv-non-isa/riscv-iommu/issues/765#issuecomment-4742941894
Best regards,
Zhanpeng
> From: "Guo Ren"<guoren@kernel.org>
> Date: Fri, Jun 19, 2026, 00:41
> Subject: [External] Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
> To: "David Laight"<david.laight.linux@gmail.com>
> Cc: "Vivian Wang"<wangruikang@iscas.ac.cn>, <zhangzhanpeng.jasper@bytedance.com>, <alex@ghiti.fr>, <aou@eecs.berkeley.edu>, <cuiyunhui@bytedance.com>, <iommu@lists.linux.dev>, <joro@8bytes.org>, <linux-kernel@vger.kernel.org>, <linux-riscv@lists.infradead.org>, <luxu.kernel@bytedance.com>, <palmer@dabbelt.com>, <pjw@kernel.org>, <robin.murphy@arm.com>, <tjeznach@rivosinc.com>, <will@kernel.org>, <yuanzhu@bytedance.com>
> On Thu, Jun 18, 2026 at 9:36 PM David Laight
> <david.laight.linux@gmail.com> wrote:
> >
> > On Thu, 18 Jun 2026 17:51:34 +0800
> > Guo Ren <guoren@kernel.org> wrote:
> >
> > > Hi Vivian,
> > >
> > > As noted in the RISC-V IOMMU Specification, Chapter 6:
> > > > Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
> > >
> > > Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
> > > not clearly defined in the current ratified RISC-V IOMMU
> > > specification. To handle this correctly, the Linux RISC-V IOMMU driver
> > > should fall back to 32-bit MMIO accesses when reading 64-bit registers
> > > (e.g., performance counters). The behavior of 32-bit MMIO accesses is
> > > more precisely defined in the RISC-V IOMMU specification.
> > >
> > > Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
> > > MMIO) based on the current ratified RISC-V IOMMU specification, and
> > > this driver does not appear to benefit from 64-bit MMIO access either.
> > > Performance is fundamentally constrained by bus latency; assuming that
> > > simply reducing the number of accesses will improve performance is an
> > > oversimplification that ignores the underlying hardware
> > > characteristics.
> >
> > If the bus latency is significant it is almost certainly worth using
> > memory accesses to avoid re-reading the hi register.
> >
> > Something like this might work:
> >
> > static volatile u32 hi_prev, lo_prev;
> >
> > u32 hi = read_reg_hi();
> > u32 lo = read_reg_lo();
> >
> > if (lo <= lo_prev || hi != hi_prev) {
> > u32 hi_tmp = read_reg_hi;
> > if (hi_tmp != hi) {
> > hi = hi_tmp;
> > lo = 0;
> > }
> > lo_prev = ~0u;
> > hi_prev = hi;
> > }
> > lo_prev = lo;
> > return (u64)hi << 32 | lo;
> >
> > It shouldn't need any locking but the accesses do need to be ordered.
> Thank you for the suggestion. However, I believe this feedback is more
> relevant to the RISC-V IOMMU HPM patchset [1], as no counter registers
> are involved in the current patchset. That said, the idea of improving
> the hi-lo-hi slow-path mechanism to better handle high-latency
> hardware scenarios is well taken and worth discussing in the
> appropriate thread.
> [1]: https://lore.kernel.org/linux-riscv/20260208063848.3547817-2-zong.li@sifive.com/
>
> P.S. The hardware I have at hand exhibits very low interconnect
> latency. And I have never observed the slow path where hi_tmp != hi
> being triggered — my approach was to remove the retry mechanism
> directly in 32-bit mmio mode and run stress tests to check whether
> perf stat produced incorrect results. That said, I may have simply
> been lucky instead of hw guarantee.
>
> --
> Best Regards
> Guo Ren
>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-26 9:18 ` Zhanpeng Zhang
0 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-06-26 9:18 UTC (permalink / raw)
To: Guo Ren, Vivian Wang
Cc: David Laight, alex, aou, cuiyunhui, iommu, joro, linux-kernel,
linux-riscv, luxu.kernel, palmer, pjw, robin.murphy, tjeznach,
will, yuanzhu, zong.li@sifive.com
Hi Vivian, Guo Ren,
Thanks for the discussion. I just caught up with the thread and the
GitHub issue [1].
I think we probably do not need to spend too much time discussing the exact
meaning of RSCV0004 around 32-bit vs 64-bit MMIO access. At least from my
side, I do not see much practical benefit in splitting the standard HID for
this distinction.
The original problem I want to solve is simple: Linux should have a compatible
and maintainable way to support IOMMUs that need 32-bit MMIO accesses for
64-bit registers, instead of taking an access fault that leads to panic.
Ideally, one generic kernel should support both access sequences without a
build-time option.
Would it be reasonable to keep RSCV0004 / riscv,iommu as the common device
identifier, and describe the required register access width as a runtime
firmware property instead? For ACPI, this might be an _DSD property on the
IOMMU device; for devicetree, it could be a matching DT property. The driver
could then check it during enumeration and select the appropriate register
accessors.
This avoids redefining the ACPI HID or compatible string, keeps one generic
kernel image, and makes the 32-bit MMIO fallback explicit in firmware. My goal
is just to implement the spec wording that "Registers that are 64-bit wide may
be accessed using either a 32-bit or a 64-bit access." in a lightweight way
that is acceptable for the Linux driver.
[1] https://github.com/riscv-non-isa/riscv-iommu/issues/765#issuecomment-4742941894
Best regards,
Zhanpeng
> From: "Guo Ren"<guoren@kernel.org>
> Date: Fri, Jun 19, 2026, 00:41
> Subject: [External] Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
> To: "David Laight"<david.laight.linux@gmail.com>
> Cc: "Vivian Wang"<wangruikang@iscas.ac.cn>, <zhangzhanpeng.jasper@bytedance.com>, <alex@ghiti.fr>, <aou@eecs.berkeley.edu>, <cuiyunhui@bytedance.com>, <iommu@lists.linux.dev>, <joro@8bytes.org>, <linux-kernel@vger.kernel.org>, <linux-riscv@lists.infradead.org>, <luxu.kernel@bytedance.com>, <palmer@dabbelt.com>, <pjw@kernel.org>, <robin.murphy@arm.com>, <tjeznach@rivosinc.com>, <will@kernel.org>, <yuanzhu@bytedance.com>
> On Thu, Jun 18, 2026 at 9:36 PM David Laight
> <david.laight.linux@gmail.com> wrote:
> >
> > On Thu, 18 Jun 2026 17:51:34 +0800
> > Guo Ren <guoren@kernel.org> wrote:
> >
> > > Hi Vivian,
> > >
> > > As noted in the RISC-V IOMMU Specification, Chapter 6:
> > > > Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
> > >
> > > Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
> > > not clearly defined in the current ratified RISC-V IOMMU
> > > specification. To handle this correctly, the Linux RISC-V IOMMU driver
> > > should fall back to 32-bit MMIO accesses when reading 64-bit registers
> > > (e.g., performance counters). The behavior of 32-bit MMIO accesses is
> > > more precisely defined in the RISC-V IOMMU specification.
> > >
> > > Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
> > > MMIO) based on the current ratified RISC-V IOMMU specification, and
> > > this driver does not appear to benefit from 64-bit MMIO access either.
> > > Performance is fundamentally constrained by bus latency; assuming that
> > > simply reducing the number of accesses will improve performance is an
> > > oversimplification that ignores the underlying hardware
> > > characteristics.
> >
> > If the bus latency is significant it is almost certainly worth using
> > memory accesses to avoid re-reading the hi register.
> >
> > Something like this might work:
> >
> > static volatile u32 hi_prev, lo_prev;
> >
> > u32 hi = read_reg_hi();
> > u32 lo = read_reg_lo();
> >
> > if (lo <= lo_prev || hi != hi_prev) {
> > u32 hi_tmp = read_reg_hi;
> > if (hi_tmp != hi) {
> > hi = hi_tmp;
> > lo = 0;
> > }
> > lo_prev = ~0u;
> > hi_prev = hi;
> > }
> > lo_prev = lo;
> > return (u64)hi << 32 | lo;
> >
> > It shouldn't need any locking but the accesses do need to be ordered.
> Thank you for the suggestion. However, I believe this feedback is more
> relevant to the RISC-V IOMMU HPM patchset [1], as no counter registers
> are involved in the current patchset. That said, the idea of improving
> the hi-lo-hi slow-path mechanism to better handle high-latency
> hardware scenarios is well taken and worth discussing in the
> appropriate thread.
> [1]: https://lore.kernel.org/linux-riscv/20260208063848.3547817-2-zong.li@sifive.com/
>
> P.S. The hardware I have at hand exhibits very low interconnect
> latency. And I have never observed the slow path where hi_tmp != hi
> being triggered — my approach was to remove the retry mechanism
> directly in 32-bit mmio mode and run stress tests to check whether
> perf stat produced incorrect results. That said, I may have simply
> been lucky instead of hw guarantee.
>
> --
> Best Regards
> Guo Ren
>
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-23 9:20 ` Zong Li
@ 2026-06-28 8:20 ` Guo Ren
-1 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-28 8:20 UTC (permalink / raw)
To: Zong Li
Cc: David Laight, Vivian Wang, zhangzhanpeng.jasper, alex, aou,
cuiyunhui, iommu, joro, linux-kernel, linux-riscv, luxu.kernel,
palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
Hi Zong Li,
On Tue, Jun 23, 2026 at 5:21 PM Zong Li <zong.li@sifive.com> wrote:
>
> On Fri, Jun 19, 2026 at 1:14 AM Guo Ren <guoren@kernel.org> wrote:
> >
> > On Thu, Jun 18, 2026 at 9:36 PM David Laight
> > <david.laight.linux@gmail.com> wrote:
> > >
> > > On Thu, 18 Jun 2026 17:51:34 +0800
> > > Guo Ren <guoren@kernel.org> wrote:
> > >
> > > > Hi Vivian,
> > > >
> > > > As noted in the RISC-V IOMMU Specification, Chapter 6:
> > > > > Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
> > > >
> > > > Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
> > > > not clearly defined in the current ratified RISC-V IOMMU
> > > > specification. To handle this correctly, the Linux RISC-V IOMMU driver
> > > > should fall back to 32-bit MMIO accesses when reading 64-bit registers
> > > > (e.g., performance counters). The behavior of 32-bit MMIO accesses is
> > > > more precisely defined in the RISC-V IOMMU specification.
> > > >
> > > > Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
> > > > MMIO) based on the current ratified RISC-V IOMMU specification, and
> > > > this driver does not appear to benefit from 64-bit MMIO access either.
> > > > Performance is fundamentally constrained by bus latency; assuming that
> > > > simply reducing the number of accesses will improve performance is an
> > > > oversimplification that ignores the underlying hardware
> > > > characteristics.
> > >
> > > If the bus latency is significant it is almost certainly worth using
> > > memory accesses to avoid re-reading the hi register.
> > >
> > > Something like this might work:
> > >
> > > static volatile u32 hi_prev, lo_prev;
> > >
> > > u32 hi = read_reg_hi();
> > > u32 lo = read_reg_lo();
> > >
> > > if (lo <= lo_prev || hi != hi_prev) {
> > > u32 hi_tmp = read_reg_hi;
> > > if (hi_tmp != hi) {
> > > hi = hi_tmp;
> > > lo = 0;
> > > }
> > > lo_prev = ~0u;
> > > hi_prev = hi;
> > > }
> > > lo_prev = lo;
> > > return (u64)hi << 32 | lo;
> > >
> > > It shouldn't need any locking but the accesses do need to be ordered.
> > Thank you for the suggestion. However, I believe this feedback is more
> > relevant to the RISC-V IOMMU HPM patchset [1], as no counter registers
> > are involved in the current patchset. That said, the idea of improving
> > the hi-lo-hi slow-path mechanism to better handle high-latency
> > hardware scenarios is well taken and worth discussing in the
> > appropriate thread.
> > [1]: https://lore.kernel.org/linux-riscv/20260208063848.3547817-2-zong.li@sifive.com/
> >
> > P.S. The hardware I have at hand exhibits very low interconnect
> > latency. And I have never observed the slow path where hi_tmp != hi
> > being triggered — my approach was to remove the retry mechanism
> > directly in 32-bit mmio mode and run stress tests to check whether
> > perf stat produced incorrect results. That said, I may have simply
> > been lucky instead of hw guarantee.
> >
>
>
> Hi everyone,
>
> Thank you for adding me to this discussion. I took some time to read
> the previous messages.
> Regarding the GitHub issue mentioned by Vivian, I noticed someone
> pointed out that the hardware must support 64-bit access.
>
> https://github.com/riscv-non-isa/riscv-iommu/issues/765#issuecomment-4742941894
>
> I would like to confirm if that is correct. If so, should we update
> the spec? And does this mean we do not need to modify the driver?
Currently, no one has proposed updating the SPEC. Discussions are
still focused on clarifying the meaning of the existing specification.
There are two main points under discussion: the first has been
clarified, while the second remains controversial.
The first point — "Whether an 8-byte access to an IOMMU register is
single-copy atomic is UNSPECIFIED" — is uncontroversial. Ved further
explained that such an access may appear internally to the IOMMU as
two separate 4-byte accesses. Therefore, for hpm COUNTER counters,
32-bit MMIO access must be used to guarantee the correct hi-lo-hi
ordering. Using 64-bit MMIO access can result in a lo-hi-hi-lo
sequence, because the specification does not define the order in which
the access may be split and explicitly marks this behavior as
UNSPECIFIED. At present, there is unanimous agreement on this point,
that is, "Whether an 8-byte access to an IOMMU register is single-copy
atomic is UNSPECIFIED".
The second point — "Registers that are 64-bit wide may be accessed
using either a 32-bit or a 64-bit access." — is controversial.
According to the clear definition in RFC 2119: MAY This word, or the
adjective "OPTIONAL", mean that an item is truly optional. One vendor
may choose to include the item because a particular marketplace
requires it or because the vendor feels that it enhances the product
while another vendor may omit the same item. An implementation which
does not include a particular option MUST be prepared to interoperate
with another implementation which does include the option, though
perhaps with reduced functionality. In the same vein an implementation
which does include a particular option MUST be prepared to
interoperate with another implementation which does not include the
option (except, of course, for the feature the option provides).
Consequently, any claim that this wording imposes a requirement for
hardware to support 64-bit MMIO access has generated significant
controversy.
The current explicit consensus on the specification is therefore as follows:
1. All hardware must support access to 64-bit registers using 32-bit
MMIO accesses.
2. Only 32-bit MMIO accesses are guaranteed to be single-copy atomic.
From both a single-copy atomicity perspective and a hardware
requirements standpoint, Linux kernel drivers should currently follow
the consensus described above and avoid the controversial 64-bit MMIO
access. Furthermore, I see no performance benefit to using 64-bit MMIO
accesses in this driver, as the only 64-bit registers involved — such
as RISCV_IOMMU_REG_DDTP, RISCV_IOMMU_REG_ICVEC, and queue->qbr — are
not frequently accessed.
--
Best Regards
Guo Ren
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-28 8:20 ` Guo Ren
0 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-06-28 8:20 UTC (permalink / raw)
To: Zong Li
Cc: David Laight, Vivian Wang, zhangzhanpeng.jasper, alex, aou,
cuiyunhui, iommu, joro, linux-kernel, linux-riscv, luxu.kernel,
palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
Hi Zong Li,
On Tue, Jun 23, 2026 at 5:21 PM Zong Li <zong.li@sifive.com> wrote:
>
> On Fri, Jun 19, 2026 at 1:14 AM Guo Ren <guoren@kernel.org> wrote:
> >
> > On Thu, Jun 18, 2026 at 9:36 PM David Laight
> > <david.laight.linux@gmail.com> wrote:
> > >
> > > On Thu, 18 Jun 2026 17:51:34 +0800
> > > Guo Ren <guoren@kernel.org> wrote:
> > >
> > > > Hi Vivian,
> > > >
> > > > As noted in the RISC-V IOMMU Specification, Chapter 6:
> > > > > Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
> > > >
> > > > Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
> > > > not clearly defined in the current ratified RISC-V IOMMU
> > > > specification. To handle this correctly, the Linux RISC-V IOMMU driver
> > > > should fall back to 32-bit MMIO accesses when reading 64-bit registers
> > > > (e.g., performance counters). The behavior of 32-bit MMIO accesses is
> > > > more precisely defined in the RISC-V IOMMU specification.
> > > >
> > > > Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
> > > > MMIO) based on the current ratified RISC-V IOMMU specification, and
> > > > this driver does not appear to benefit from 64-bit MMIO access either.
> > > > Performance is fundamentally constrained by bus latency; assuming that
> > > > simply reducing the number of accesses will improve performance is an
> > > > oversimplification that ignores the underlying hardware
> > > > characteristics.
> > >
> > > If the bus latency is significant it is almost certainly worth using
> > > memory accesses to avoid re-reading the hi register.
> > >
> > > Something like this might work:
> > >
> > > static volatile u32 hi_prev, lo_prev;
> > >
> > > u32 hi = read_reg_hi();
> > > u32 lo = read_reg_lo();
> > >
> > > if (lo <= lo_prev || hi != hi_prev) {
> > > u32 hi_tmp = read_reg_hi;
> > > if (hi_tmp != hi) {
> > > hi = hi_tmp;
> > > lo = 0;
> > > }
> > > lo_prev = ~0u;
> > > hi_prev = hi;
> > > }
> > > lo_prev = lo;
> > > return (u64)hi << 32 | lo;
> > >
> > > It shouldn't need any locking but the accesses do need to be ordered.
> > Thank you for the suggestion. However, I believe this feedback is more
> > relevant to the RISC-V IOMMU HPM patchset [1], as no counter registers
> > are involved in the current patchset. That said, the idea of improving
> > the hi-lo-hi slow-path mechanism to better handle high-latency
> > hardware scenarios is well taken and worth discussing in the
> > appropriate thread.
> > [1]: https://lore.kernel.org/linux-riscv/20260208063848.3547817-2-zong.li@sifive.com/
> >
> > P.S. The hardware I have at hand exhibits very low interconnect
> > latency. And I have never observed the slow path where hi_tmp != hi
> > being triggered — my approach was to remove the retry mechanism
> > directly in 32-bit mmio mode and run stress tests to check whether
> > perf stat produced incorrect results. That said, I may have simply
> > been lucky instead of hw guarantee.
> >
>
>
> Hi everyone,
>
> Thank you for adding me to this discussion. I took some time to read
> the previous messages.
> Regarding the GitHub issue mentioned by Vivian, I noticed someone
> pointed out that the hardware must support 64-bit access.
>
> https://github.com/riscv-non-isa/riscv-iommu/issues/765#issuecomment-4742941894
>
> I would like to confirm if that is correct. If so, should we update
> the spec? And does this mean we do not need to modify the driver?
Currently, no one has proposed updating the SPEC. Discussions are
still focused on clarifying the meaning of the existing specification.
There are two main points under discussion: the first has been
clarified, while the second remains controversial.
The first point — "Whether an 8-byte access to an IOMMU register is
single-copy atomic is UNSPECIFIED" — is uncontroversial. Ved further
explained that such an access may appear internally to the IOMMU as
two separate 4-byte accesses. Therefore, for hpm COUNTER counters,
32-bit MMIO access must be used to guarantee the correct hi-lo-hi
ordering. Using 64-bit MMIO access can result in a lo-hi-hi-lo
sequence, because the specification does not define the order in which
the access may be split and explicitly marks this behavior as
UNSPECIFIED. At present, there is unanimous agreement on this point,
that is, "Whether an 8-byte access to an IOMMU register is single-copy
atomic is UNSPECIFIED".
The second point — "Registers that are 64-bit wide may be accessed
using either a 32-bit or a 64-bit access." — is controversial.
According to the clear definition in RFC 2119: MAY This word, or the
adjective "OPTIONAL", mean that an item is truly optional. One vendor
may choose to include the item because a particular marketplace
requires it or because the vendor feels that it enhances the product
while another vendor may omit the same item. An implementation which
does not include a particular option MUST be prepared to interoperate
with another implementation which does include the option, though
perhaps with reduced functionality. In the same vein an implementation
which does include a particular option MUST be prepared to
interoperate with another implementation which does not include the
option (except, of course, for the feature the option provides).
Consequently, any claim that this wording imposes a requirement for
hardware to support 64-bit MMIO access has generated significant
controversy.
The current explicit consensus on the specification is therefore as follows:
1. All hardware must support access to 64-bit registers using 32-bit
MMIO accesses.
2. Only 32-bit MMIO accesses are guaranteed to be single-copy atomic.
From both a single-copy atomicity perspective and a hardware
requirements standpoint, Linux kernel drivers should currently follow
the consensus described above and avoid the controversial 64-bit MMIO
access. Furthermore, I see no performance benefit to using 64-bit MMIO
accesses in this driver, as the only 64-bit registers involved — such
as RISCV_IOMMU_REG_DDTP, RISCV_IOMMU_REG_ICVEC, and queue->qbr — are
not frequently accessed.
--
Best Regards
Guo Ren
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
2026-06-28 8:20 ` Guo Ren
@ 2026-06-29 1:15 ` Zong Li
-1 siblings, 0 replies; 70+ messages in thread
From: Zong Li @ 2026-06-29 1:15 UTC (permalink / raw)
To: Guo Ren
Cc: David Laight, Vivian Wang, zhangzhanpeng.jasper, alex, aou,
cuiyunhui, iommu, joro, linux-kernel, linux-riscv, luxu.kernel,
palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
On Sun, Jun 28, 2026 at 4:20 PM Guo Ren <guoren@kernel.org> wrote:
>
> Hi Zong Li,
>
> On Tue, Jun 23, 2026 at 5:21 PM Zong Li <zong.li@sifive.com> wrote:
> >
> > On Fri, Jun 19, 2026 at 1:14 AM Guo Ren <guoren@kernel.org> wrote:
> > >
> > > On Thu, Jun 18, 2026 at 9:36 PM David Laight
> > > <david.laight.linux@gmail.com> wrote:
> > > >
> > > > On Thu, 18 Jun 2026 17:51:34 +0800
> > > > Guo Ren <guoren@kernel.org> wrote:
> > > >
> > > > > Hi Vivian,
> > > > >
> > > > > As noted in the RISC-V IOMMU Specification, Chapter 6:
> > > > > > Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
> > > > >
> > > > > Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
> > > > > not clearly defined in the current ratified RISC-V IOMMU
> > > > > specification. To handle this correctly, the Linux RISC-V IOMMU driver
> > > > > should fall back to 32-bit MMIO accesses when reading 64-bit registers
> > > > > (e.g., performance counters). The behavior of 32-bit MMIO accesses is
> > > > > more precisely defined in the RISC-V IOMMU specification.
> > > > >
> > > > > Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
> > > > > MMIO) based on the current ratified RISC-V IOMMU specification, and
> > > > > this driver does not appear to benefit from 64-bit MMIO access either.
> > > > > Performance is fundamentally constrained by bus latency; assuming that
> > > > > simply reducing the number of accesses will improve performance is an
> > > > > oversimplification that ignores the underlying hardware
> > > > > characteristics.
> > > >
> > > > If the bus latency is significant it is almost certainly worth using
> > > > memory accesses to avoid re-reading the hi register.
> > > >
> > > > Something like this might work:
> > > >
> > > > static volatile u32 hi_prev, lo_prev;
> > > >
> > > > u32 hi = read_reg_hi();
> > > > u32 lo = read_reg_lo();
> > > >
> > > > if (lo <= lo_prev || hi != hi_prev) {
> > > > u32 hi_tmp = read_reg_hi;
> > > > if (hi_tmp != hi) {
> > > > hi = hi_tmp;
> > > > lo = 0;
> > > > }
> > > > lo_prev = ~0u;
> > > > hi_prev = hi;
> > > > }
> > > > lo_prev = lo;
> > > > return (u64)hi << 32 | lo;
> > > >
> > > > It shouldn't need any locking but the accesses do need to be ordered.
> > > Thank you for the suggestion. However, I believe this feedback is more
> > > relevant to the RISC-V IOMMU HPM patchset [1], as no counter registers
> > > are involved in the current patchset. That said, the idea of improving
> > > the hi-lo-hi slow-path mechanism to better handle high-latency
> > > hardware scenarios is well taken and worth discussing in the
> > > appropriate thread.
> > > [1]: https://lore.kernel.org/linux-riscv/20260208063848.3547817-2-zong.li@sifive.com/
> > >
> > > P.S. The hardware I have at hand exhibits very low interconnect
> > > latency. And I have never observed the slow path where hi_tmp != hi
> > > being triggered — my approach was to remove the retry mechanism
> > > directly in 32-bit mmio mode and run stress tests to check whether
> > > perf stat produced incorrect results. That said, I may have simply
> > > been lucky instead of hw guarantee.
> > >
> >
> >
> > Hi everyone,
> >
> > Thank you for adding me to this discussion. I took some time to read
> > the previous messages.
> > Regarding the GitHub issue mentioned by Vivian, I noticed someone
> > pointed out that the hardware must support 64-bit access.
> >
> > https://github.com/riscv-non-isa/riscv-iommu/issues/765#issuecomment-4742941894
> >
> > I would like to confirm if that is correct. If so, should we update
> > the spec? And does this mean we do not need to modify the driver?
>
> Currently, no one has proposed updating the SPEC. Discussions are
> still focused on clarifying the meaning of the existing specification.
> There are two main points under discussion: the first has been
> clarified, while the second remains controversial.
>
> The first point — "Whether an 8-byte access to an IOMMU register is
> single-copy atomic is UNSPECIFIED" — is uncontroversial. Ved further
> explained that such an access may appear internally to the IOMMU as
> two separate 4-byte accesses. Therefore, for hpm COUNTER counters,
> 32-bit MMIO access must be used to guarantee the correct hi-lo-hi
> ordering. Using 64-bit MMIO access can result in a lo-hi-hi-lo
> sequence, because the specification does not define the order in which
> the access may be split and explicitly marks this behavior as
> UNSPECIFIED. At present, there is unanimous agreement on this point,
> that is, "Whether an 8-byte access to an IOMMU register is single-copy
> atomic is UNSPECIFIED".
>
> The second point — "Registers that are 64-bit wide may be accessed
> using either a 32-bit or a 64-bit access." — is controversial.
> According to the clear definition in RFC 2119: MAY This word, or the
> adjective "OPTIONAL", mean that an item is truly optional. One vendor
> may choose to include the item because a particular marketplace
> requires it or because the vendor feels that it enhances the product
> while another vendor may omit the same item. An implementation which
> does not include a particular option MUST be prepared to interoperate
> with another implementation which does include the option, though
> perhaps with reduced functionality. In the same vein an implementation
> which does include a particular option MUST be prepared to
> interoperate with another implementation which does not include the
> option (except, of course, for the feature the option provides).
> Consequently, any claim that this wording imposes a requirement for
> hardware to support 64-bit MMIO access has generated significant
> controversy.
>
> The current explicit consensus on the specification is therefore as follows:
> 1. All hardware must support access to 64-bit registers using 32-bit
> MMIO accesses.
> 2. Only 32-bit MMIO accesses are guaranteed to be single-copy atomic.
>
> From both a single-copy atomicity perspective and a hardware
> requirements standpoint, Linux kernel drivers should currently follow
> the consensus described above and avoid the controversial 64-bit MMIO
> access. Furthermore, I see no performance benefit to using 64-bit MMIO
> accesses in this driver, as the only 64-bit registers involved — such
> as RISCV_IOMMU_REG_DDTP, RISCV_IOMMU_REG_ICVEC, and queue->qbr — are
> not frequently accessed.
>
Thanks for your clarification, I will address this topic in PMU driver
in the next version.
> --
> Best Regards
> Guo Ren
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v1] iommu/riscv: Support 32-bit register accesses
@ 2026-06-29 1:15 ` Zong Li
0 siblings, 0 replies; 70+ messages in thread
From: Zong Li @ 2026-06-29 1:15 UTC (permalink / raw)
To: Guo Ren
Cc: David Laight, Vivian Wang, zhangzhanpeng.jasper, alex, aou,
cuiyunhui, iommu, joro, linux-kernel, linux-riscv, luxu.kernel,
palmer, pjw, robin.murphy, tjeznach, will, yuanzhu
On Sun, Jun 28, 2026 at 4:20 PM Guo Ren <guoren@kernel.org> wrote:
>
> Hi Zong Li,
>
> On Tue, Jun 23, 2026 at 5:21 PM Zong Li <zong.li@sifive.com> wrote:
> >
> > On Fri, Jun 19, 2026 at 1:14 AM Guo Ren <guoren@kernel.org> wrote:
> > >
> > > On Thu, Jun 18, 2026 at 9:36 PM David Laight
> > > <david.laight.linux@gmail.com> wrote:
> > > >
> > > > On Thu, 18 Jun 2026 17:51:34 +0800
> > > > Guo Ren <guoren@kernel.org> wrote:
> > > >
> > > > > Hi Vivian,
> > > > >
> > > > > As noted in the RISC-V IOMMU Specification, Chapter 6:
> > > > > > Whether an 8-byte access to an IOMMU register is single-copy atomic is UNSPECIFIED, and such an access may appear, internally to the IOMMU, as if two separate 4-byte accesses — first to the high half and second to the low half — were performed.
> > > > >
> > > > > Therefore, the atomicity of 64-bit MMIO accesses is UNSPECIFIED and
> > > > > not clearly defined in the current ratified RISC-V IOMMU
> > > > > specification. To handle this correctly, the Linux RISC-V IOMMU driver
> > > > > should fall back to 32-bit MMIO accesses when reading 64-bit registers
> > > > > (e.g., performance counters). The behavior of 32-bit MMIO accesses is
> > > > > more precisely defined in the RISC-V IOMMU specification.
> > > > >
> > > > > Thus, many hardware vendors implement 32-bit MMIO (rather than 64-bit
> > > > > MMIO) based on the current ratified RISC-V IOMMU specification, and
> > > > > this driver does not appear to benefit from 64-bit MMIO access either.
> > > > > Performance is fundamentally constrained by bus latency; assuming that
> > > > > simply reducing the number of accesses will improve performance is an
> > > > > oversimplification that ignores the underlying hardware
> > > > > characteristics.
> > > >
> > > > If the bus latency is significant it is almost certainly worth using
> > > > memory accesses to avoid re-reading the hi register.
> > > >
> > > > Something like this might work:
> > > >
> > > > static volatile u32 hi_prev, lo_prev;
> > > >
> > > > u32 hi = read_reg_hi();
> > > > u32 lo = read_reg_lo();
> > > >
> > > > if (lo <= lo_prev || hi != hi_prev) {
> > > > u32 hi_tmp = read_reg_hi;
> > > > if (hi_tmp != hi) {
> > > > hi = hi_tmp;
> > > > lo = 0;
> > > > }
> > > > lo_prev = ~0u;
> > > > hi_prev = hi;
> > > > }
> > > > lo_prev = lo;
> > > > return (u64)hi << 32 | lo;
> > > >
> > > > It shouldn't need any locking but the accesses do need to be ordered.
> > > Thank you for the suggestion. However, I believe this feedback is more
> > > relevant to the RISC-V IOMMU HPM patchset [1], as no counter registers
> > > are involved in the current patchset. That said, the idea of improving
> > > the hi-lo-hi slow-path mechanism to better handle high-latency
> > > hardware scenarios is well taken and worth discussing in the
> > > appropriate thread.
> > > [1]: https://lore.kernel.org/linux-riscv/20260208063848.3547817-2-zong.li@sifive.com/
> > >
> > > P.S. The hardware I have at hand exhibits very low interconnect
> > > latency. And I have never observed the slow path where hi_tmp != hi
> > > being triggered — my approach was to remove the retry mechanism
> > > directly in 32-bit mmio mode and run stress tests to check whether
> > > perf stat produced incorrect results. That said, I may have simply
> > > been lucky instead of hw guarantee.
> > >
> >
> >
> > Hi everyone,
> >
> > Thank you for adding me to this discussion. I took some time to read
> > the previous messages.
> > Regarding the GitHub issue mentioned by Vivian, I noticed someone
> > pointed out that the hardware must support 64-bit access.
> >
> > https://github.com/riscv-non-isa/riscv-iommu/issues/765#issuecomment-4742941894
> >
> > I would like to confirm if that is correct. If so, should we update
> > the spec? And does this mean we do not need to modify the driver?
>
> Currently, no one has proposed updating the SPEC. Discussions are
> still focused on clarifying the meaning of the existing specification.
> There are two main points under discussion: the first has been
> clarified, while the second remains controversial.
>
> The first point — "Whether an 8-byte access to an IOMMU register is
> single-copy atomic is UNSPECIFIED" — is uncontroversial. Ved further
> explained that such an access may appear internally to the IOMMU as
> two separate 4-byte accesses. Therefore, for hpm COUNTER counters,
> 32-bit MMIO access must be used to guarantee the correct hi-lo-hi
> ordering. Using 64-bit MMIO access can result in a lo-hi-hi-lo
> sequence, because the specification does not define the order in which
> the access may be split and explicitly marks this behavior as
> UNSPECIFIED. At present, there is unanimous agreement on this point,
> that is, "Whether an 8-byte access to an IOMMU register is single-copy
> atomic is UNSPECIFIED".
>
> The second point — "Registers that are 64-bit wide may be accessed
> using either a 32-bit or a 64-bit access." — is controversial.
> According to the clear definition in RFC 2119: MAY This word, or the
> adjective "OPTIONAL", mean that an item is truly optional. One vendor
> may choose to include the item because a particular marketplace
> requires it or because the vendor feels that it enhances the product
> while another vendor may omit the same item. An implementation which
> does not include a particular option MUST be prepared to interoperate
> with another implementation which does include the option, though
> perhaps with reduced functionality. In the same vein an implementation
> which does include a particular option MUST be prepared to
> interoperate with another implementation which does not include the
> option (except, of course, for the feature the option provides).
> Consequently, any claim that this wording imposes a requirement for
> hardware to support 64-bit MMIO access has generated significant
> controversy.
>
> The current explicit consensus on the specification is therefore as follows:
> 1. All hardware must support access to 64-bit registers using 32-bit
> MMIO accesses.
> 2. Only 32-bit MMIO accesses are guaranteed to be single-copy atomic.
>
> From both a single-copy atomicity perspective and a hardware
> requirements standpoint, Linux kernel drivers should currently follow
> the consensus described above and avoid the controversial 64-bit MMIO
> access. Furthermore, I see no performance benefit to using 64-bit MMIO
> accesses in this driver, as the only 64-bit registers involved — such
> as RISCV_IOMMU_REG_DDTP, RISCV_IOMMU_REG_ICVEC, and queue->qbr — are
> not frequently accessed.
>
Thanks for your clarification, I will address this topic in PMU driver
in the next version.
> --
> Best Regards
> Guo Ren
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* [PATCH v2] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
2026-06-15 6:48 ` Zhanpeng Zhang
@ 2026-07-13 6:09 ` Zhanpeng Zhang
-1 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-07-13 6:09 UTC (permalink / raw)
To: Tomasz Jeznach, Joerg Roedel, Will Deacon
Cc: Robin Murphy, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Guo Ren, David Laight, Vivian Wang, Zong Li,
cuiyunhui, yuanzhu, iommu, linux-riscv, linux-kernel,
Zhanpeng Zhang
The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
using two 32-bit transactions, high half first, and leaves the single-copy
atomicity of 8-byte IOMMU register accesses unspecified.
Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
for ordinary 64-bit IOMMU registers. Poll DDTP.BUSY in the low half before
reading the full register from high to low. HPM counter reads require a
rollover-aware sequence and remain outside these accessors.
This follows the 32-bit access direction proposed by Guo Ren [2] and uses
the generic non-atomic MMIO helpers suggested by David Laight.
[1] https://docs.riscv.org/reference/iommu/
[2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
Suggested-by: Guo Ren <guoren@kernel.org>
Suggested-by: David Laight <david.laight.linux@gmail.com>
Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
---
Changes in v2:
- Rework the patch based on Guo Ren's earlier proposal [1].
- Drop the build-time option and use 32-bit accesses unconditionally.
- Drop the global lock and use the generic high-low MMIO helpers, as
suggested by David Laight.
- Poll DDTP.BUSY through its low half before reading the full register.
Link to v1: [2]
Specification discussion: [3]
[1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
[2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
[3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
drivers/iommu/riscv/iommu.c | 18 +++++++++++-------
drivers/iommu/riscv/iommu.h | 9 +++------
2 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index cec3ddd7ab1..14b572058ed 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -668,12 +668,16 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
riscv_iommu_writel(iommu, RISCV_IOMMU_REG_PQCSR, 0);
}
-#define riscv_iommu_read_ddtp(iommu) ({ \
- u64 ddtp; \
- riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
- !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
- RISCV_IOMMU_DDTP_TIMEOUT); \
- ddtp; })
+static u64 riscv_iommu_read_ddtp(struct riscv_iommu_device *iommu)
+{
+ u32 ddtp_lo;
+
+ riscv_iommu_readl_timeout(iommu, RISCV_IOMMU_REG_DDTP, ddtp_lo,
+ !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10,
+ RISCV_IOMMU_DDTP_TIMEOUT);
+
+ return riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
+}
static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
{
@@ -1501,7 +1505,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
* regular boot flow and disable translation when we boot into a kexec
* kernel and the previous kernel left them enabled.
*/
- ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
+ ddtp = riscv_iommu_read_ddtp(iommu);
if (ddtp & RISCV_IOMMU_DDTP_BUSY)
return -EBUSY;
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 46df79dd549..1b03790fbe1 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -11,6 +11,7 @@
#ifndef _RISCV_IOMMU_H_
#define _RISCV_IOMMU_H_
+#include <linux/io-64-nonatomic-hi-lo.h>
#include <linux/iommu.h>
#include <linux/types.h>
#include <linux/iopoll.h>
@@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
readl_relaxed((iommu)->reg + (addr))
#define riscv_iommu_readq(iommu, addr) \
- readq_relaxed((iommu)->reg + (addr))
+ hi_lo_readq_relaxed((iommu)->reg + (addr))
#define riscv_iommu_writel(iommu, addr, val) \
writel_relaxed((val), (iommu)->reg + (addr))
#define riscv_iommu_writeq(iommu, addr, val) \
- writeq_relaxed((val), (iommu)->reg + (addr))
-
-#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
- readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
- delay_us, timeout_us)
+ hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
--
2.50.1 (Apple Git-155)
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
@ 2026-07-13 6:09 ` Zhanpeng Zhang
0 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-07-13 6:09 UTC (permalink / raw)
To: Tomasz Jeznach, Joerg Roedel, Will Deacon
Cc: Robin Murphy, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Guo Ren, David Laight, Vivian Wang, Zong Li,
cuiyunhui, yuanzhu, iommu, linux-riscv, linux-kernel,
Zhanpeng Zhang
The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
using two 32-bit transactions, high half first, and leaves the single-copy
atomicity of 8-byte IOMMU register accesses unspecified.
Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
for ordinary 64-bit IOMMU registers. Poll DDTP.BUSY in the low half before
reading the full register from high to low. HPM counter reads require a
rollover-aware sequence and remain outside these accessors.
This follows the 32-bit access direction proposed by Guo Ren [2] and uses
the generic non-atomic MMIO helpers suggested by David Laight.
[1] https://docs.riscv.org/reference/iommu/
[2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
Suggested-by: Guo Ren <guoren@kernel.org>
Suggested-by: David Laight <david.laight.linux@gmail.com>
Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
---
Changes in v2:
- Rework the patch based on Guo Ren's earlier proposal [1].
- Drop the build-time option and use 32-bit accesses unconditionally.
- Drop the global lock and use the generic high-low MMIO helpers, as
suggested by David Laight.
- Poll DDTP.BUSY through its low half before reading the full register.
Link to v1: [2]
Specification discussion: [3]
[1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
[2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
[3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
drivers/iommu/riscv/iommu.c | 18 +++++++++++-------
drivers/iommu/riscv/iommu.h | 9 +++------
2 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index cec3ddd7ab1..14b572058ed 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -668,12 +668,16 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
riscv_iommu_writel(iommu, RISCV_IOMMU_REG_PQCSR, 0);
}
-#define riscv_iommu_read_ddtp(iommu) ({ \
- u64 ddtp; \
- riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
- !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
- RISCV_IOMMU_DDTP_TIMEOUT); \
- ddtp; })
+static u64 riscv_iommu_read_ddtp(struct riscv_iommu_device *iommu)
+{
+ u32 ddtp_lo;
+
+ riscv_iommu_readl_timeout(iommu, RISCV_IOMMU_REG_DDTP, ddtp_lo,
+ !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10,
+ RISCV_IOMMU_DDTP_TIMEOUT);
+
+ return riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
+}
static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
{
@@ -1501,7 +1505,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
* regular boot flow and disable translation when we boot into a kexec
* kernel and the previous kernel left them enabled.
*/
- ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
+ ddtp = riscv_iommu_read_ddtp(iommu);
if (ddtp & RISCV_IOMMU_DDTP_BUSY)
return -EBUSY;
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 46df79dd549..1b03790fbe1 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -11,6 +11,7 @@
#ifndef _RISCV_IOMMU_H_
#define _RISCV_IOMMU_H_
+#include <linux/io-64-nonatomic-hi-lo.h>
#include <linux/iommu.h>
#include <linux/types.h>
#include <linux/iopoll.h>
@@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
readl_relaxed((iommu)->reg + (addr))
#define riscv_iommu_readq(iommu, addr) \
- readq_relaxed((iommu)->reg + (addr))
+ hi_lo_readq_relaxed((iommu)->reg + (addr))
#define riscv_iommu_writel(iommu, addr, val) \
writel_relaxed((val), (iommu)->reg + (addr))
#define riscv_iommu_writeq(iommu, addr, val) \
- writeq_relaxed((val), (iommu)->reg + (addr))
-
-#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
- readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
- delay_us, timeout_us)
+ hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 70+ messages in thread
* Re: [PATCH v2] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
2026-07-13 6:09 ` Zhanpeng Zhang
@ 2026-07-13 7:00 ` Guo Ren
-1 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-07-13 7:00 UTC (permalink / raw)
To: Zhanpeng Zhang
Cc: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
David Laight, Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu,
linux-riscv, linux-kernel
On Mon, Jul 13, 2026 at 2:09 PM Zhanpeng Zhang
<zhangzhanpeng.jasper@bytedance.com> wrote:
>
> The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
> using two 32-bit transactions, high half first, and leaves the single-copy
> atomicity of 8-byte IOMMU register accesses unspecified.
>
> Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
> for ordinary 64-bit IOMMU registers. Poll DDTP.BUSY in the low half before
> reading the full register from high to low. HPM counter reads require a
> rollover-aware sequence and remain outside these accessors.
>
> This follows the 32-bit access direction proposed by Guo Ren [2] and uses
> the generic non-atomic MMIO helpers suggested by David Laight.
>
> [1] https://docs.riscv.org/reference/iommu/
> [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
>
> Suggested-by: Guo Ren <guoren@kernel.org>
> Suggested-by: David Laight <david.laight.linux@gmail.com>
> Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> ---
> Changes in v2:
> - Rework the patch based on Guo Ren's earlier proposal [1].
> - Drop the build-time option and use 32-bit accesses unconditionally.
> - Drop the global lock and use the generic high-low MMIO helpers, as
> suggested by David Laight.
> - Poll DDTP.BUSY through its low half before reading the full register.
>
> Link to v1: [2]
> Specification discussion: [3]
>
> [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
> [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
>
> drivers/iommu/riscv/iommu.c | 18 +++++++++++-------
> drivers/iommu/riscv/iommu.h | 9 +++------
> 2 files changed, 14 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index cec3ddd7ab1..14b572058ed 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -668,12 +668,16 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
> riscv_iommu_writel(iommu, RISCV_IOMMU_REG_PQCSR, 0);
> }
>
> -#define riscv_iommu_read_ddtp(iommu) ({ \
> - u64 ddtp; \
> - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> - RISCV_IOMMU_DDTP_TIMEOUT); \
> - ddtp; })
> +static u64 riscv_iommu_read_ddtp(struct riscv_iommu_device *iommu)
> +{
> + u32 ddtp_lo;
> +
> + riscv_iommu_readl_timeout(iommu, RISCV_IOMMU_REG_DDTP, ddtp_lo,
> + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10,
> + RISCV_IOMMU_DDTP_TIMEOUT);
> +
> + return riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
readq? Please ref [1]:
#define riscv_iommu_read_ddtp(iommu) ({ \
u64 ddtp; \
- riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
- !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
+ u32 ddtp_lo, ddtp_hi; \
+ riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
+ !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
RISCV_IOMMU_DDTP_TIMEOUT); \
+ ddtp_hi = riscv_iommu_readl(iommu, RISCV_IOMMU_REG_DDTP + 4); \
+ ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
ddtp; })
> +}
>
> static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
> {
> @@ -1501,7 +1505,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> * regular boot flow and disable translation when we boot into a kexec
> * kernel and the previous kernel left them enabled.
> */
> - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> + ddtp = riscv_iommu_read_ddtp(iommu);
> if (ddtp & RISCV_IOMMU_DDTP_BUSY)
> return -EBUSY;
>
> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> index 46df79dd549..1b03790fbe1 100644
> --- a/drivers/iommu/riscv/iommu.h
> +++ b/drivers/iommu/riscv/iommu.h
> @@ -11,6 +11,7 @@
> #ifndef _RISCV_IOMMU_H_
> #define _RISCV_IOMMU_H_
>
> +#include <linux/io-64-nonatomic-hi-lo.h>
> #include <linux/iommu.h>
> #include <linux/types.h>
> #include <linux/iopoll.h>
> @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> readl_relaxed((iommu)->reg + (addr))
>
> #define riscv_iommu_readq(iommu, addr) \
> - readq_relaxed((iommu)->reg + (addr))
> + hi_lo_readq_relaxed((iommu)->reg + (addr))
>
> #define riscv_iommu_writel(iommu, addr, val) \
> writel_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_writeq(iommu, addr, val) \
> - writeq_relaxed((val), (iommu)->reg + (addr))
> -
> -#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> - readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> - delay_us, timeout_us)
> + hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> --
> 2.50.1 (Apple Git-155)
--
Best Regards
Guo Ren
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
@ 2026-07-13 7:00 ` Guo Ren
0 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-07-13 7:00 UTC (permalink / raw)
To: Zhanpeng Zhang
Cc: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
David Laight, Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu,
linux-riscv, linux-kernel
On Mon, Jul 13, 2026 at 2:09 PM Zhanpeng Zhang
<zhangzhanpeng.jasper@bytedance.com> wrote:
>
> The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
> using two 32-bit transactions, high half first, and leaves the single-copy
> atomicity of 8-byte IOMMU register accesses unspecified.
>
> Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
> for ordinary 64-bit IOMMU registers. Poll DDTP.BUSY in the low half before
> reading the full register from high to low. HPM counter reads require a
> rollover-aware sequence and remain outside these accessors.
>
> This follows the 32-bit access direction proposed by Guo Ren [2] and uses
> the generic non-atomic MMIO helpers suggested by David Laight.
>
> [1] https://docs.riscv.org/reference/iommu/
> [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
>
> Suggested-by: Guo Ren <guoren@kernel.org>
> Suggested-by: David Laight <david.laight.linux@gmail.com>
> Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> ---
> Changes in v2:
> - Rework the patch based on Guo Ren's earlier proposal [1].
> - Drop the build-time option and use 32-bit accesses unconditionally.
> - Drop the global lock and use the generic high-low MMIO helpers, as
> suggested by David Laight.
> - Poll DDTP.BUSY through its low half before reading the full register.
>
> Link to v1: [2]
> Specification discussion: [3]
>
> [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
> [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
>
> drivers/iommu/riscv/iommu.c | 18 +++++++++++-------
> drivers/iommu/riscv/iommu.h | 9 +++------
> 2 files changed, 14 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index cec3ddd7ab1..14b572058ed 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -668,12 +668,16 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
> riscv_iommu_writel(iommu, RISCV_IOMMU_REG_PQCSR, 0);
> }
>
> -#define riscv_iommu_read_ddtp(iommu) ({ \
> - u64 ddtp; \
> - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> - RISCV_IOMMU_DDTP_TIMEOUT); \
> - ddtp; })
> +static u64 riscv_iommu_read_ddtp(struct riscv_iommu_device *iommu)
> +{
> + u32 ddtp_lo;
> +
> + riscv_iommu_readl_timeout(iommu, RISCV_IOMMU_REG_DDTP, ddtp_lo,
> + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10,
> + RISCV_IOMMU_DDTP_TIMEOUT);
> +
> + return riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
readq? Please ref [1]:
#define riscv_iommu_read_ddtp(iommu) ({ \
u64 ddtp; \
- riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
- !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
+ u32 ddtp_lo, ddtp_hi; \
+ riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
+ !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
RISCV_IOMMU_DDTP_TIMEOUT); \
+ ddtp_hi = riscv_iommu_readl(iommu, RISCV_IOMMU_REG_DDTP + 4); \
+ ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
ddtp; })
> +}
>
> static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
> {
> @@ -1501,7 +1505,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> * regular boot flow and disable translation when we boot into a kexec
> * kernel and the previous kernel left them enabled.
> */
> - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> + ddtp = riscv_iommu_read_ddtp(iommu);
> if (ddtp & RISCV_IOMMU_DDTP_BUSY)
> return -EBUSY;
>
> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> index 46df79dd549..1b03790fbe1 100644
> --- a/drivers/iommu/riscv/iommu.h
> +++ b/drivers/iommu/riscv/iommu.h
> @@ -11,6 +11,7 @@
> #ifndef _RISCV_IOMMU_H_
> #define _RISCV_IOMMU_H_
>
> +#include <linux/io-64-nonatomic-hi-lo.h>
> #include <linux/iommu.h>
> #include <linux/types.h>
> #include <linux/iopoll.h>
> @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> readl_relaxed((iommu)->reg + (addr))
>
> #define riscv_iommu_readq(iommu, addr) \
> - readq_relaxed((iommu)->reg + (addr))
> + hi_lo_readq_relaxed((iommu)->reg + (addr))
>
> #define riscv_iommu_writel(iommu, addr, val) \
> writel_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_writeq(iommu, addr, val) \
> - writeq_relaxed((val), (iommu)->reg + (addr))
> -
> -#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> - readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> - delay_us, timeout_us)
> + hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> --
> 2.50.1 (Apple Git-155)
--
Best Regards
Guo Ren
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: Re: [PATCH v2] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
2026-07-13 7:00 ` Guo Ren
@ 2026-07-13 8:24 ` Zhanpeng Zhang
-1 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-07-13 8:24 UTC (permalink / raw)
To: Guo Ren
Cc: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
David Laight, Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu,
linux-riscv, linux-kernel
Hi Guo,
On 7/13/26 3:00 PM, Guo Ren wrote:
> On Mon, Jul 13, 2026 at 2:09 PM Zhanpeng Zhang
> <zhangzhanpeng.jasper@bytedance.com> wrote:
>>
>> The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
>> using two 32-bit transactions, high half first, and leaves the single-copy
>> atomicity of 8-byte IOMMU register accesses unspecified.
>>
>> Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
>> for ordinary 64-bit IOMMU registers. Poll DDTP.BUSY in the low half before
>> reading the full register from high to low. HPM counter reads require a
>> rollover-aware sequence and remain outside these accessors.
>>
>> This follows the 32-bit access direction proposed by Guo Ren [2] and uses
>> the generic non-atomic MMIO helpers suggested by David Laight.
>>
>> [1] https://docs.riscv.org/reference/iommu/
>> [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
>>
>> Suggested-by: Guo Ren <guoren@kernel.org>
>> Suggested-by: David Laight <david.laight.linux@gmail.com>
>> Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
>> ---
>> Changes in v2:
>> - Rework the patch based on Guo Ren's earlier proposal [1].
>> - Drop the build-time option and use 32-bit accesses unconditionally.
>> - Drop the global lock and use the generic high-low MMIO helpers, as
>> suggested by David Laight.
>> - Poll DDTP.BUSY through its low half before reading the full register.
>>
>> Link to v1: [2]
>> Specification discussion: [3]
>>
>> [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
>> [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
>> [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
>>
>> drivers/iommu/riscv/iommu.c | 18 +++++++++++-------
>> drivers/iommu/riscv/iommu.h | 9 +++------
>> 2 files changed, 14 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
>> index cec3ddd7ab1..14b572058ed 100644
>> --- a/drivers/iommu/riscv/iommu.c
>> +++ b/drivers/iommu/riscv/iommu.c
>> @@ -668,12 +668,16 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
>> riscv_iommu_writel(iommu, RISCV_IOMMU_REG_PQCSR, 0);
>> }
>>
>> -#define riscv_iommu_read_ddtp(iommu) ({ \
>> - u64 ddtp; \
>> - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
>> - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
>> - RISCV_IOMMU_DDTP_TIMEOUT); \
>> - ddtp; })
>> +static u64 riscv_iommu_read_ddtp(struct riscv_iommu_device *iommu)
>> +{
>> + u32 ddtp_lo;
>> +
>> + riscv_iommu_readl_timeout(iommu, RISCV_IOMMU_REG_DDTP, ddtp_lo,
>> + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10,
>> + RISCV_IOMMU_DDTP_TIMEOUT);
>> +
>> + return riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> readq? Please ref [1]:
>
> #define riscv_iommu_read_ddtp(iommu) ({ \
> u64 ddtp; \
> - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> + u32 ddtp_lo, ddtp_hi; \
> + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
> + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
> RISCV_IOMMU_DDTP_TIMEOUT); \
> + ddtp_hi = riscv_iommu_readl(iommu, RISCV_IOMMU_REG_DDTP + 4); \
> + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
> ddtp; })
>
Oops, thanks for catching that, it will re-read the low 32 bits. DDTP
must retain that polled low-half value, read only the high half, and
then compose the result.
The macro one [1] is correct, I've fixed it in v3.
>
>> +}
>>
>> static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
>> {
>> @@ -1501,7 +1505,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
>> * regular boot flow and disable translation when we boot into a kexec
>> * kernel and the previous kernel left them enabled.
>> */
>> - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
>> + ddtp = riscv_iommu_read_ddtp(iommu);
>> if (ddtp & RISCV_IOMMU_DDTP_BUSY)
>> return -EBUSY;
>>
>> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
>> index 46df79dd549..1b03790fbe1 100644
>> --- a/drivers/iommu/riscv/iommu.h
>> +++ b/drivers/iommu/riscv/iommu.h
>> @@ -11,6 +11,7 @@
>> #ifndef _RISCV_IOMMU_H_
>> #define _RISCV_IOMMU_H_
>>
>> +#include <linux/io-64-nonatomic-hi-lo.h>
>> #include <linux/iommu.h>
>> #include <linux/types.h>
>> #include <linux/iopoll.h>
>> @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
>> readl_relaxed((iommu)->reg + (addr))
>>
>> #define riscv_iommu_readq(iommu, addr) \
>> - readq_relaxed((iommu)->reg + (addr))
>> + hi_lo_readq_relaxed((iommu)->reg + (addr))
>>
>> #define riscv_iommu_writel(iommu, addr, val) \
>> writel_relaxed((val), (iommu)->reg + (addr))
>>
>> #define riscv_iommu_writeq(iommu, addr, val) \
>> - writeq_relaxed((val), (iommu)->reg + (addr))
>> -
>> -#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
>> - readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
>> - delay_us, timeout_us)
>> + hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
>>
>> #define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
>> readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
>> --
>> 2.50.1 (Apple Git-155)
>
>
>
Thanks,
Zhanpeng
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: Re: [PATCH v2] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
@ 2026-07-13 8:24 ` Zhanpeng Zhang
0 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-07-13 8:24 UTC (permalink / raw)
To: Guo Ren
Cc: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
David Laight, Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu,
linux-riscv, linux-kernel
Hi Guo,
On 7/13/26 3:00 PM, Guo Ren wrote:
> On Mon, Jul 13, 2026 at 2:09 PM Zhanpeng Zhang
> <zhangzhanpeng.jasper@bytedance.com> wrote:
>>
>> The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
>> using two 32-bit transactions, high half first, and leaves the single-copy
>> atomicity of 8-byte IOMMU register accesses unspecified.
>>
>> Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
>> for ordinary 64-bit IOMMU registers. Poll DDTP.BUSY in the low half before
>> reading the full register from high to low. HPM counter reads require a
>> rollover-aware sequence and remain outside these accessors.
>>
>> This follows the 32-bit access direction proposed by Guo Ren [2] and uses
>> the generic non-atomic MMIO helpers suggested by David Laight.
>>
>> [1] https://docs.riscv.org/reference/iommu/
>> [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
>>
>> Suggested-by: Guo Ren <guoren@kernel.org>
>> Suggested-by: David Laight <david.laight.linux@gmail.com>
>> Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
>> ---
>> Changes in v2:
>> - Rework the patch based on Guo Ren's earlier proposal [1].
>> - Drop the build-time option and use 32-bit accesses unconditionally.
>> - Drop the global lock and use the generic high-low MMIO helpers, as
>> suggested by David Laight.
>> - Poll DDTP.BUSY through its low half before reading the full register.
>>
>> Link to v1: [2]
>> Specification discussion: [3]
>>
>> [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
>> [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
>> [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
>>
>> drivers/iommu/riscv/iommu.c | 18 +++++++++++-------
>> drivers/iommu/riscv/iommu.h | 9 +++------
>> 2 files changed, 14 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
>> index cec3ddd7ab1..14b572058ed 100644
>> --- a/drivers/iommu/riscv/iommu.c
>> +++ b/drivers/iommu/riscv/iommu.c
>> @@ -668,12 +668,16 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
>> riscv_iommu_writel(iommu, RISCV_IOMMU_REG_PQCSR, 0);
>> }
>>
>> -#define riscv_iommu_read_ddtp(iommu) ({ \
>> - u64 ddtp; \
>> - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
>> - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
>> - RISCV_IOMMU_DDTP_TIMEOUT); \
>> - ddtp; })
>> +static u64 riscv_iommu_read_ddtp(struct riscv_iommu_device *iommu)
>> +{
>> + u32 ddtp_lo;
>> +
>> + riscv_iommu_readl_timeout(iommu, RISCV_IOMMU_REG_DDTP, ddtp_lo,
>> + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10,
>> + RISCV_IOMMU_DDTP_TIMEOUT);
>> +
>> + return riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> readq? Please ref [1]:
>
> #define riscv_iommu_read_ddtp(iommu) ({ \
> u64 ddtp; \
> - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> + u32 ddtp_lo, ddtp_hi; \
> + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
> + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
> RISCV_IOMMU_DDTP_TIMEOUT); \
> + ddtp_hi = riscv_iommu_readl(iommu, RISCV_IOMMU_REG_DDTP + 4); \
> + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
> ddtp; })
>
Oops, thanks for catching that, it will re-read the low 32 bits. DDTP
must retain that polled low-half value, read only the high half, and
then compose the result.
The macro one [1] is correct, I've fixed it in v3.
>
>> +}
>>
>> static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
>> {
>> @@ -1501,7 +1505,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
>> * regular boot flow and disable translation when we boot into a kexec
>> * kernel and the previous kernel left them enabled.
>> */
>> - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
>> + ddtp = riscv_iommu_read_ddtp(iommu);
>> if (ddtp & RISCV_IOMMU_DDTP_BUSY)
>> return -EBUSY;
>>
>> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
>> index 46df79dd549..1b03790fbe1 100644
>> --- a/drivers/iommu/riscv/iommu.h
>> +++ b/drivers/iommu/riscv/iommu.h
>> @@ -11,6 +11,7 @@
>> #ifndef _RISCV_IOMMU_H_
>> #define _RISCV_IOMMU_H_
>>
>> +#include <linux/io-64-nonatomic-hi-lo.h>
>> #include <linux/iommu.h>
>> #include <linux/types.h>
>> #include <linux/iopoll.h>
>> @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
>> readl_relaxed((iommu)->reg + (addr))
>>
>> #define riscv_iommu_readq(iommu, addr) \
>> - readq_relaxed((iommu)->reg + (addr))
>> + hi_lo_readq_relaxed((iommu)->reg + (addr))
>>
>> #define riscv_iommu_writel(iommu, addr, val) \
>> writel_relaxed((val), (iommu)->reg + (addr))
>>
>> #define riscv_iommu_writeq(iommu, addr, val) \
>> - writeq_relaxed((val), (iommu)->reg + (addr))
>> -
>> -#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
>> - readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
>> - delay_us, timeout_us)
>> + hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
>>
>> #define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
>> readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
>> --
>> 2.50.1 (Apple Git-155)
>
>
>
Thanks,
Zhanpeng
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
2026-06-15 6:48 ` Zhanpeng Zhang
@ 2026-07-13 12:29 ` Zhanpeng Zhang
-1 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-07-13 12:29 UTC (permalink / raw)
To: Tomasz Jeznach, Joerg Roedel, Will Deacon
Cc: Robin Murphy, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Guo Ren, David Laight, Vivian Wang, Zong Li,
cuiyunhui, yuanzhu, iommu, linux-riscv, linux-kernel
The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
using two 32-bit transactions, high half first, and leaves the single-copy
atomicity of 8-byte IOMMU register accesses unspecified.
Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
then read the high half and compose the register value from the polled low
half. HPM counter reads require a rollover-aware sequence and remain
outside these accessors.
This follows the 32-bit access direction proposed by Guo Ren [2] and uses
the generic non-atomic MMIO helpers suggested by David Laight.
[1] https://docs.riscv.org/reference/iommu/
[2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
Suggested-by: Guo Ren <guoren@kernel.org>
Suggested-by: David Laight <david.laight.linux@gmail.com>
Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
---
Changes in v3:
- Use the DDTP access sequence from [1]: retain the low half returned by
BUSY polling, read only the high half, and compose the DDTP value from
those two 32-bit reads.
Changes in v2:
- Rework the patch based on Guo Ren's earlier proposal [1].
- Drop the build-time option and use 32-bit accesses unconditionally.
- Drop the global lock and use the generic high-low MMIO helpers, as
suggested by David Laight.
- Poll DDTP.BUSY through its low half.
Link to v1: [2]
Specification discussion: [3]
[1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
[2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
[3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
drivers/iommu/riscv/iommu.c | 9 ++++++---
drivers/iommu/riscv/iommu.h | 9 +++------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index cec3ddd7ab1..d647b71ebec 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
#define riscv_iommu_read_ddtp(iommu) ({ \
u64 ddtp; \
- riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
- !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
+ u32 ddtp_lo, ddtp_hi; \
+ riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
+ !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
RISCV_IOMMU_DDTP_TIMEOUT); \
+ ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
+ ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
ddtp; })
static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
@@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
* regular boot flow and disable translation when we boot into a kexec
* kernel and the previous kernel left them enabled.
*/
- ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
+ ddtp = riscv_iommu_read_ddtp(iommu);
if (ddtp & RISCV_IOMMU_DDTP_BUSY)
return -EBUSY;
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 46df79dd549..1b03790fbe1 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -11,6 +11,7 @@
#ifndef _RISCV_IOMMU_H_
#define _RISCV_IOMMU_H_
+#include <linux/io-64-nonatomic-hi-lo.h>
#include <linux/iommu.h>
#include <linux/types.h>
#include <linux/iopoll.h>
@@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
readl_relaxed((iommu)->reg + (addr))
#define riscv_iommu_readq(iommu, addr) \
- readq_relaxed((iommu)->reg + (addr))
+ hi_lo_readq_relaxed((iommu)->reg + (addr))
#define riscv_iommu_writel(iommu, addr, val) \
writel_relaxed((val), (iommu)->reg + (addr))
#define riscv_iommu_writeq(iommu, addr, val) \
- writeq_relaxed((val), (iommu)->reg + (addr))
-
-#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
- readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
- delay_us, timeout_us)
+ hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
@ 2026-07-13 12:29 ` Zhanpeng Zhang
0 siblings, 0 replies; 70+ messages in thread
From: Zhanpeng Zhang @ 2026-07-13 12:29 UTC (permalink / raw)
To: Tomasz Jeznach, Joerg Roedel, Will Deacon
Cc: Robin Murphy, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Guo Ren, David Laight, Vivian Wang, Zong Li,
cuiyunhui, yuanzhu, iommu, linux-riscv, linux-kernel
The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
using two 32-bit transactions, high half first, and leaves the single-copy
atomicity of 8-byte IOMMU register accesses unspecified.
Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
then read the high half and compose the register value from the polled low
half. HPM counter reads require a rollover-aware sequence and remain
outside these accessors.
This follows the 32-bit access direction proposed by Guo Ren [2] and uses
the generic non-atomic MMIO helpers suggested by David Laight.
[1] https://docs.riscv.org/reference/iommu/
[2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
Suggested-by: Guo Ren <guoren@kernel.org>
Suggested-by: David Laight <david.laight.linux@gmail.com>
Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
---
Changes in v3:
- Use the DDTP access sequence from [1]: retain the low half returned by
BUSY polling, read only the high half, and compose the DDTP value from
those two 32-bit reads.
Changes in v2:
- Rework the patch based on Guo Ren's earlier proposal [1].
- Drop the build-time option and use 32-bit accesses unconditionally.
- Drop the global lock and use the generic high-low MMIO helpers, as
suggested by David Laight.
- Poll DDTP.BUSY through its low half.
Link to v1: [2]
Specification discussion: [3]
[1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
[2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
[3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
drivers/iommu/riscv/iommu.c | 9 ++++++---
drivers/iommu/riscv/iommu.h | 9 +++------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index cec3ddd7ab1..d647b71ebec 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
#define riscv_iommu_read_ddtp(iommu) ({ \
u64 ddtp; \
- riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
- !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
+ u32 ddtp_lo, ddtp_hi; \
+ riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
+ !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
RISCV_IOMMU_DDTP_TIMEOUT); \
+ ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
+ ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
ddtp; })
static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
@@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
* regular boot flow and disable translation when we boot into a kexec
* kernel and the previous kernel left them enabled.
*/
- ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
+ ddtp = riscv_iommu_read_ddtp(iommu);
if (ddtp & RISCV_IOMMU_DDTP_BUSY)
return -EBUSY;
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 46df79dd549..1b03790fbe1 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -11,6 +11,7 @@
#ifndef _RISCV_IOMMU_H_
#define _RISCV_IOMMU_H_
+#include <linux/io-64-nonatomic-hi-lo.h>
#include <linux/iommu.h>
#include <linux/types.h>
#include <linux/iopoll.h>
@@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
readl_relaxed((iommu)->reg + (addr))
#define riscv_iommu_readq(iommu, addr) \
- readq_relaxed((iommu)->reg + (addr))
+ hi_lo_readq_relaxed((iommu)->reg + (addr))
#define riscv_iommu_writel(iommu, addr, val) \
writel_relaxed((val), (iommu)->reg + (addr))
#define riscv_iommu_writeq(iommu, addr, val) \
- writeq_relaxed((val), (iommu)->reg + (addr))
-
-#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
- readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
- delay_us, timeout_us)
+ hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
--
2.50.1 (Apple Git-155)
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 70+ messages in thread
* Re: [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
2026-07-13 12:29 ` Zhanpeng Zhang
@ 2026-07-14 2:02 ` Guo Ren
-1 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-07-14 2:02 UTC (permalink / raw)
To: Zhanpeng Zhang
Cc: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
David Laight, Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu,
linux-riscv, linux-kernel
LGTM!
Reviewed-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
Co-developed-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
Signed-off-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
On Mon, Jul 13, 2026 at 8:29 PM Zhanpeng Zhang
<zhangzhanpeng.jasper@bytedance.com> wrote:
>
> The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
> using two 32-bit transactions, high half first, and leaves the single-copy
> atomicity of 8-byte IOMMU register accesses unspecified.
>
> Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
> for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
> then read the high half and compose the register value from the polled low
> half. HPM counter reads require a rollover-aware sequence and remain
> outside these accessors.
>
> This follows the 32-bit access direction proposed by Guo Ren [2] and uses
> the generic non-atomic MMIO helpers suggested by David Laight.
>
> [1] https://docs.riscv.org/reference/iommu/
> [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
>
> Suggested-by: Guo Ren <guoren@kernel.org>
> Suggested-by: David Laight <david.laight.linux@gmail.com>
> Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> ---
> Changes in v3:
> - Use the DDTP access sequence from [1]: retain the low half returned by
> BUSY polling, read only the high half, and compose the DDTP value from
> those two 32-bit reads.
>
> Changes in v2:
> - Rework the patch based on Guo Ren's earlier proposal [1].
> - Drop the build-time option and use 32-bit accesses unconditionally.
> - Drop the global lock and use the generic high-low MMIO helpers, as
> suggested by David Laight.
> - Poll DDTP.BUSY through its low half.
>
> Link to v1: [2]
> Specification discussion: [3]
>
> [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
> [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
>
> drivers/iommu/riscv/iommu.c | 9 ++++++---
> drivers/iommu/riscv/iommu.h | 9 +++------
> 2 files changed, 9 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index cec3ddd7ab1..d647b71ebec 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
>
> #define riscv_iommu_read_ddtp(iommu) ({ \
> u64 ddtp; \
> - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> + u32 ddtp_lo, ddtp_hi; \
> + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
> + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
> RISCV_IOMMU_DDTP_TIMEOUT); \
> + ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
> + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
> ddtp; })
>
> static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
> @@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> * regular boot flow and disable translation when we boot into a kexec
> * kernel and the previous kernel left them enabled.
> */
> - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> + ddtp = riscv_iommu_read_ddtp(iommu);
> if (ddtp & RISCV_IOMMU_DDTP_BUSY)
> return -EBUSY;
>
> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> index 46df79dd549..1b03790fbe1 100644
> --- a/drivers/iommu/riscv/iommu.h
> +++ b/drivers/iommu/riscv/iommu.h
> @@ -11,6 +11,7 @@
> #ifndef _RISCV_IOMMU_H_
> #define _RISCV_IOMMU_H_
>
> +#include <linux/io-64-nonatomic-hi-lo.h>
> #include <linux/iommu.h>
> #include <linux/types.h>
> #include <linux/iopoll.h>
> @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> readl_relaxed((iommu)->reg + (addr))
>
> #define riscv_iommu_readq(iommu, addr) \
> - readq_relaxed((iommu)->reg + (addr))
> + hi_lo_readq_relaxed((iommu)->reg + (addr))
>
> #define riscv_iommu_writel(iommu, addr, val) \
> writel_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_writeq(iommu, addr, val) \
> - writeq_relaxed((val), (iommu)->reg + (addr))
> -
> -#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> - readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> - delay_us, timeout_us)
> + hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> --
> 2.50.1 (Apple Git-155)
--
Best Regards
Guo Ren
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
@ 2026-07-14 2:02 ` Guo Ren
0 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-07-14 2:02 UTC (permalink / raw)
To: Zhanpeng Zhang
Cc: Tomasz Jeznach, Joerg Roedel, Will Deacon, Robin Murphy,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
David Laight, Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu,
linux-riscv, linux-kernel
LGTM!
Reviewed-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
Co-developed-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
Signed-off-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
On Mon, Jul 13, 2026 at 8:29 PM Zhanpeng Zhang
<zhangzhanpeng.jasper@bytedance.com> wrote:
>
> The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
> using two 32-bit transactions, high half first, and leaves the single-copy
> atomicity of 8-byte IOMMU register accesses unspecified.
>
> Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
> for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
> then read the high half and compose the register value from the polled low
> half. HPM counter reads require a rollover-aware sequence and remain
> outside these accessors.
>
> This follows the 32-bit access direction proposed by Guo Ren [2] and uses
> the generic non-atomic MMIO helpers suggested by David Laight.
>
> [1] https://docs.riscv.org/reference/iommu/
> [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
>
> Suggested-by: Guo Ren <guoren@kernel.org>
> Suggested-by: David Laight <david.laight.linux@gmail.com>
> Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> ---
> Changes in v3:
> - Use the DDTP access sequence from [1]: retain the low half returned by
> BUSY polling, read only the high half, and compose the DDTP value from
> those two 32-bit reads.
>
> Changes in v2:
> - Rework the patch based on Guo Ren's earlier proposal [1].
> - Drop the build-time option and use 32-bit accesses unconditionally.
> - Drop the global lock and use the generic high-low MMIO helpers, as
> suggested by David Laight.
> - Poll DDTP.BUSY through its low half.
>
> Link to v1: [2]
> Specification discussion: [3]
>
> [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
> [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
>
> drivers/iommu/riscv/iommu.c | 9 ++++++---
> drivers/iommu/riscv/iommu.h | 9 +++------
> 2 files changed, 9 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index cec3ddd7ab1..d647b71ebec 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
>
> #define riscv_iommu_read_ddtp(iommu) ({ \
> u64 ddtp; \
> - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> + u32 ddtp_lo, ddtp_hi; \
> + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
> + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
> RISCV_IOMMU_DDTP_TIMEOUT); \
> + ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
> + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
> ddtp; })
>
> static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
> @@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> * regular boot flow and disable translation when we boot into a kexec
> * kernel and the previous kernel left them enabled.
> */
> - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> + ddtp = riscv_iommu_read_ddtp(iommu);
> if (ddtp & RISCV_IOMMU_DDTP_BUSY)
> return -EBUSY;
>
> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> index 46df79dd549..1b03790fbe1 100644
> --- a/drivers/iommu/riscv/iommu.h
> +++ b/drivers/iommu/riscv/iommu.h
> @@ -11,6 +11,7 @@
> #ifndef _RISCV_IOMMU_H_
> #define _RISCV_IOMMU_H_
>
> +#include <linux/io-64-nonatomic-hi-lo.h>
> #include <linux/iommu.h>
> #include <linux/types.h>
> #include <linux/iopoll.h>
> @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> readl_relaxed((iommu)->reg + (addr))
>
> #define riscv_iommu_readq(iommu, addr) \
> - readq_relaxed((iommu)->reg + (addr))
> + hi_lo_readq_relaxed((iommu)->reg + (addr))
>
> #define riscv_iommu_writel(iommu, addr, val) \
> writel_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_writeq(iommu, addr, val) \
> - writeq_relaxed((val), (iommu)->reg + (addr))
> -
> -#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> - readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> - delay_us, timeout_us)
> + hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
> --
> 2.50.1 (Apple Git-155)
--
Best Regards
Guo Ren
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
2026-07-14 2:02 ` Guo Ren
@ 2026-07-14 7:10 ` Tomasz Jeznach
-1 siblings, 0 replies; 70+ messages in thread
From: Tomasz Jeznach @ 2026-07-14 7:10 UTC (permalink / raw)
To: Guo Ren, Zhanpeng Zhang
Cc: Joerg Roedel, Will Deacon, Robin Murphy, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, David Laight,
Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu, linux-riscv,
linux-kernel
On 7/13/26 7:02 PM, Guo Ren wrote:
> LGTM!
>
> Reviewed-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
> Co-developed-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
> Signed-off-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
>
> On Mon, Jul 13, 2026 at 8:29 PM Zhanpeng Zhang
> <zhangzhanpeng.jasper@bytedance.com> wrote:
>> The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
>> using two 32-bit transactions, high half first, and leaves the single-copy
>> atomicity of 8-byte IOMMU register accesses unspecified.
>>
>> Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
>> for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
>> then read the high half and compose the register value from the polled low
>> half. HPM counter reads require a rollover-aware sequence and remain
>> outside these accessors.
>>
>> This follows the 32-bit access direction proposed by Guo Ren [2] and uses
>> the generic non-atomic MMIO helpers suggested by David Laight.
>>
>> [1] https://docs.riscv.org/reference/iommu/
>> [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
>>
>> Suggested-by: Guo Ren <guoren@kernel.org>
>> Suggested-by: David Laight <david.laight.linux@gmail.com>
>> Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
>> ---
>> Changes in v3:
>> - Use the DDTP access sequence from [1]: retain the low half returned by
>> BUSY polling, read only the high half, and compose the DDTP value from
>> those two 32-bit reads.
>>
>> Changes in v2:
>> - Rework the patch based on Guo Ren's earlier proposal [1].
>> - Drop the build-time option and use 32-bit accesses unconditionally.
>> - Drop the global lock and use the generic high-low MMIO helpers, as
>> suggested by David Laight.
>> - Poll DDTP.BUSY through its low half.
>>
>> Link to v1: [2]
>> Specification discussion: [3]
>>
>> [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
>> [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
>> [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
>>
>> drivers/iommu/riscv/iommu.c | 9 ++++++---
>> drivers/iommu/riscv/iommu.h | 9 +++------
>> 2 files changed, 9 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
>> index cec3ddd7ab1..d647b71ebec 100644
>> --- a/drivers/iommu/riscv/iommu.c
>> +++ b/drivers/iommu/riscv/iommu.c
>> @@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
>>
>> #define riscv_iommu_read_ddtp(iommu) ({ \
>> u64 ddtp; \
>> - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
>> - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
>> + u32 ddtp_lo, ddtp_hi; \
>> + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
>> + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
>> RISCV_IOMMU_DDTP_TIMEOUT); \
>> + ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
>> + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
>> ddtp; })
>>
>> static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
>> @@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
>> * regular boot flow and disable translation when we boot into a kexec
>> * kernel and the previous kernel left them enabled.
>> */
>> - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
>> + ddtp = riscv_iommu_read_ddtp(iommu);
>> if (ddtp & RISCV_IOMMU_DDTP_BUSY)
>> return -EBUSY;
>>
>> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
>> index 46df79dd549..1b03790fbe1 100644
>> --- a/drivers/iommu/riscv/iommu.h
>> +++ b/drivers/iommu/riscv/iommu.h
>> @@ -11,6 +11,7 @@
>> #ifndef _RISCV_IOMMU_H_
>> #define _RISCV_IOMMU_H_
>>
>> +#include <linux/io-64-nonatomic-hi-lo.h>
>> #include <linux/iommu.h>
>> #include <linux/types.h>
>> #include <linux/iopoll.h>
>> @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
>> readl_relaxed((iommu)->reg + (addr))
>>
>> #define riscv_iommu_readq(iommu, addr) \
>> - readq_relaxed((iommu)->reg + (addr))
>> + hi_lo_readq_relaxed((iommu)->reg + (addr))
>>
>> #define riscv_iommu_writel(iommu, addr, val) \
>> writel_relaxed((val), (iommu)->reg + (addr))
>>
>> #define riscv_iommu_writeq(iommu, addr, val) \
>> - writeq_relaxed((val), (iommu)->reg + (addr))
>> -
>> -#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
>> - readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
>> - delay_us, timeout_us)
>> + hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
>>
>> #define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
>> readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
>> --
>> 2.50.1 (Apple Git-155)
Thank you for this change. Sorry for being late on the discussion. LGTM
Reviewed-by: Tomasz Jeznach <tomasz.jeznach@linux.dev>
Best,
- Tomasz
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
@ 2026-07-14 7:10 ` Tomasz Jeznach
0 siblings, 0 replies; 70+ messages in thread
From: Tomasz Jeznach @ 2026-07-14 7:10 UTC (permalink / raw)
To: Guo Ren, Zhanpeng Zhang
Cc: Joerg Roedel, Will Deacon, Robin Murphy, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, David Laight,
Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu, linux-riscv,
linux-kernel
On 7/13/26 7:02 PM, Guo Ren wrote:
> LGTM!
>
> Reviewed-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
> Co-developed-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
> Signed-off-by: Guo Ren (Alibaba DAMO Academy) <guoren@kernel.org>
>
> On Mon, Jul 13, 2026 at 8:29 PM Zhanpeng Zhang
> <zhangzhanpeng.jasper@bytedance.com> wrote:
>> The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
>> using two 32-bit transactions, high half first, and leaves the single-copy
>> atomicity of 8-byte IOMMU register accesses unspecified.
>>
>> Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
>> for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
>> then read the high half and compose the register value from the polled low
>> half. HPM counter reads require a rollover-aware sequence and remain
>> outside these accessors.
>>
>> This follows the 32-bit access direction proposed by Guo Ren [2] and uses
>> the generic non-atomic MMIO helpers suggested by David Laight.
>>
>> [1] https://docs.riscv.org/reference/iommu/
>> [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
>>
>> Suggested-by: Guo Ren <guoren@kernel.org>
>> Suggested-by: David Laight <david.laight.linux@gmail.com>
>> Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
>> ---
>> Changes in v3:
>> - Use the DDTP access sequence from [1]: retain the low half returned by
>> BUSY polling, read only the high half, and compose the DDTP value from
>> those two 32-bit reads.
>>
>> Changes in v2:
>> - Rework the patch based on Guo Ren's earlier proposal [1].
>> - Drop the build-time option and use 32-bit accesses unconditionally.
>> - Drop the global lock and use the generic high-low MMIO helpers, as
>> suggested by David Laight.
>> - Poll DDTP.BUSY through its low half.
>>
>> Link to v1: [2]
>> Specification discussion: [3]
>>
>> [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
>> [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
>> [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
>>
>> drivers/iommu/riscv/iommu.c | 9 ++++++---
>> drivers/iommu/riscv/iommu.h | 9 +++------
>> 2 files changed, 9 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
>> index cec3ddd7ab1..d647b71ebec 100644
>> --- a/drivers/iommu/riscv/iommu.c
>> +++ b/drivers/iommu/riscv/iommu.c
>> @@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
>>
>> #define riscv_iommu_read_ddtp(iommu) ({ \
>> u64 ddtp; \
>> - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
>> - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
>> + u32 ddtp_lo, ddtp_hi; \
>> + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
>> + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
>> RISCV_IOMMU_DDTP_TIMEOUT); \
>> + ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
>> + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
>> ddtp; })
>>
>> static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
>> @@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
>> * regular boot flow and disable translation when we boot into a kexec
>> * kernel and the previous kernel left them enabled.
>> */
>> - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
>> + ddtp = riscv_iommu_read_ddtp(iommu);
>> if (ddtp & RISCV_IOMMU_DDTP_BUSY)
>> return -EBUSY;
>>
>> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
>> index 46df79dd549..1b03790fbe1 100644
>> --- a/drivers/iommu/riscv/iommu.h
>> +++ b/drivers/iommu/riscv/iommu.h
>> @@ -11,6 +11,7 @@
>> #ifndef _RISCV_IOMMU_H_
>> #define _RISCV_IOMMU_H_
>>
>> +#include <linux/io-64-nonatomic-hi-lo.h>
>> #include <linux/iommu.h>
>> #include <linux/types.h>
>> #include <linux/iopoll.h>
>> @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
>> readl_relaxed((iommu)->reg + (addr))
>>
>> #define riscv_iommu_readq(iommu, addr) \
>> - readq_relaxed((iommu)->reg + (addr))
>> + hi_lo_readq_relaxed((iommu)->reg + (addr))
>>
>> #define riscv_iommu_writel(iommu, addr, val) \
>> writel_relaxed((val), (iommu)->reg + (addr))
>>
>> #define riscv_iommu_writeq(iommu, addr, val) \
>> - writeq_relaxed((val), (iommu)->reg + (addr))
>> -
>> -#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
>> - readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
>> - delay_us, timeout_us)
>> + hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
>>
>> #define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
>> readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
>> --
>> 2.50.1 (Apple Git-155)
Thank you for this change. Sorry for being late on the discussion. LGTM
Reviewed-by: Tomasz Jeznach <tomasz.jeznach@linux.dev>
Best,
- Tomasz
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
2026-07-13 12:29 ` Zhanpeng Zhang
@ 2026-07-14 12:27 ` Robin Murphy
-1 siblings, 0 replies; 70+ messages in thread
From: Robin Murphy @ 2026-07-14 12:27 UTC (permalink / raw)
To: Zhanpeng Zhang, Tomasz Jeznach, Joerg Roedel, Will Deacon
Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Guo Ren, David Laight, Vivian Wang, Zong Li, cuiyunhui, yuanzhu,
iommu, linux-riscv, linux-kernel
On 13/07/2026 1:29 pm, Zhanpeng Zhang wrote:
> The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
> using two 32-bit transactions, high half first, and leaves the single-copy
> atomicity of 8-byte IOMMU register accesses unspecified.
>
> Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
> for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
> then read the high half and compose the register value from the polled low
> half. HPM counter reads require a rollover-aware sequence and remain
> outside these accessors.
>
> This follows the 32-bit access direction proposed by Guo Ren [2] and uses
> the generic non-atomic MMIO helpers suggested by David Laight.
>
> [1] https://docs.riscv.org/reference/iommu/
> [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
>
> Suggested-by: Guo Ren <guoren@kernel.org>
> Suggested-by: David Laight <david.laight.linux@gmail.com>
> Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> ---
> Changes in v3:
> - Use the DDTP access sequence from [1]: retain the low half returned by
> BUSY polling, read only the high half, and compose the DDTP value from
> those two 32-bit reads.
>
> Changes in v2:
> - Rework the patch based on Guo Ren's earlier proposal [1].
> - Drop the build-time option and use 32-bit accesses unconditionally.
> - Drop the global lock and use the generic high-low MMIO helpers, as
> suggested by David Laight.
> - Poll DDTP.BUSY through its low half.
>
> Link to v1: [2]
> Specification discussion: [3]
>
> [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
> [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
>
> drivers/iommu/riscv/iommu.c | 9 ++++++---
> drivers/iommu/riscv/iommu.h | 9 +++------
> 2 files changed, 9 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index cec3ddd7ab1..d647b71ebec 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
>
> #define riscv_iommu_read_ddtp(iommu) ({ \
> u64 ddtp; \
> - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> + u32 ddtp_lo, ddtp_hi; \
> + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
> + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
> RISCV_IOMMU_DDTP_TIMEOUT); \
> + ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
It looks like whenever you read DDTP you're only really looking at the
BUSY/MODE fields anyway, so does this actually need to read the upper
bits of PPN at all? (The spec says they don't even need to be written on
RV32 either)
> + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
> ddtp; })
>
> static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
> @@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> * regular boot flow and disable translation when we boot into a kexec
> * kernel and the previous kernel left them enabled.
> */
> - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> + ddtp = riscv_iommu_read_ddtp(iommu);
> if (ddtp & RISCV_IOMMU_DDTP_BUSY)
> return -EBUSY;
>
> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> index 46df79dd549..1b03790fbe1 100644
> --- a/drivers/iommu/riscv/iommu.h
> +++ b/drivers/iommu/riscv/iommu.h
> @@ -11,6 +11,7 @@
> #ifndef _RISCV_IOMMU_H_
> #define _RISCV_IOMMU_H_
>
> +#include <linux/io-64-nonatomic-hi-lo.h>
> #include <linux/iommu.h>
> #include <linux/types.h>
> #include <linux/iopoll.h>
> @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> readl_relaxed((iommu)->reg + (addr))
>
> #define riscv_iommu_readq(iommu, addr) \
> - readq_relaxed((iommu)->reg + (addr))
> + hi_lo_readq_relaxed((iommu)->reg + (addr))
This seems unnecessary (similarly for writeq() below) - once you've
included the header, then it automatically provides readq{_relaxed}()
for RV32 via the non-atomic implementation, and wherever atomicity
doesn't matter, then as written there seems to be no reason for RV64 to
stop using the regular arch readq().
Not that it makes any difference to me either way, but I don't see any
real issue with the spec - if software *may* make a 64-bit access to any
64-bit register unconditionally, then that can only imply that hardware
*must* be able to accommodate RV64 software choosing to do so, and
therefore must support *both* 32b and 64b accesses in general, except
perhaps on RV32-only systems if software could never make 64b accesses
in the first place. If 64b single-copy atomicity is not
required/specified then it should be valid to achieve that by just
sticking a downsizer in the upstream interconnect to split 64b accesses
into 32bx2 incremental bursts.
If do actually you need this as an erratum workaround to support
specific hardware which has misinterpreted the spec then I think you
should be clear about that. Otherwise, adding speculative "workarounds"
which (slightly) penalise hardware that got it right, while inviting
future hardware to get it wrong, doesn't seem like the right way to go
at all...
Thanks,
Robin.
>
> #define riscv_iommu_writel(iommu, addr, val) \
> writel_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_writeq(iommu, addr, val) \
> - writeq_relaxed((val), (iommu)->reg + (addr))
> -
> -#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> - readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> - delay_us, timeout_us)
> + hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
@ 2026-07-14 12:27 ` Robin Murphy
0 siblings, 0 replies; 70+ messages in thread
From: Robin Murphy @ 2026-07-14 12:27 UTC (permalink / raw)
To: Zhanpeng Zhang, Tomasz Jeznach, Joerg Roedel, Will Deacon
Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Guo Ren, David Laight, Vivian Wang, Zong Li, cuiyunhui, yuanzhu,
iommu, linux-riscv, linux-kernel
On 13/07/2026 1:29 pm, Zhanpeng Zhang wrote:
> The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
> using two 32-bit transactions, high half first, and leaves the single-copy
> atomicity of 8-byte IOMMU register accesses unspecified.
>
> Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
> for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
> then read the high half and compose the register value from the polled low
> half. HPM counter reads require a rollover-aware sequence and remain
> outside these accessors.
>
> This follows the 32-bit access direction proposed by Guo Ren [2] and uses
> the generic non-atomic MMIO helpers suggested by David Laight.
>
> [1] https://docs.riscv.org/reference/iommu/
> [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
>
> Suggested-by: Guo Ren <guoren@kernel.org>
> Suggested-by: David Laight <david.laight.linux@gmail.com>
> Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> ---
> Changes in v3:
> - Use the DDTP access sequence from [1]: retain the low half returned by
> BUSY polling, read only the high half, and compose the DDTP value from
> those two 32-bit reads.
>
> Changes in v2:
> - Rework the patch based on Guo Ren's earlier proposal [1].
> - Drop the build-time option and use 32-bit accesses unconditionally.
> - Drop the global lock and use the generic high-low MMIO helpers, as
> suggested by David Laight.
> - Poll DDTP.BUSY through its low half.
>
> Link to v1: [2]
> Specification discussion: [3]
>
> [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
> [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
>
> drivers/iommu/riscv/iommu.c | 9 ++++++---
> drivers/iommu/riscv/iommu.h | 9 +++------
> 2 files changed, 9 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index cec3ddd7ab1..d647b71ebec 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
>
> #define riscv_iommu_read_ddtp(iommu) ({ \
> u64 ddtp; \
> - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> + u32 ddtp_lo, ddtp_hi; \
> + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
> + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
> RISCV_IOMMU_DDTP_TIMEOUT); \
> + ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
It looks like whenever you read DDTP you're only really looking at the
BUSY/MODE fields anyway, so does this actually need to read the upper
bits of PPN at all? (The spec says they don't even need to be written on
RV32 either)
> + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
> ddtp; })
>
> static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
> @@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> * regular boot flow and disable translation when we boot into a kexec
> * kernel and the previous kernel left them enabled.
> */
> - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> + ddtp = riscv_iommu_read_ddtp(iommu);
> if (ddtp & RISCV_IOMMU_DDTP_BUSY)
> return -EBUSY;
>
> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> index 46df79dd549..1b03790fbe1 100644
> --- a/drivers/iommu/riscv/iommu.h
> +++ b/drivers/iommu/riscv/iommu.h
> @@ -11,6 +11,7 @@
> #ifndef _RISCV_IOMMU_H_
> #define _RISCV_IOMMU_H_
>
> +#include <linux/io-64-nonatomic-hi-lo.h>
> #include <linux/iommu.h>
> #include <linux/types.h>
> #include <linux/iopoll.h>
> @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> readl_relaxed((iommu)->reg + (addr))
>
> #define riscv_iommu_readq(iommu, addr) \
> - readq_relaxed((iommu)->reg + (addr))
> + hi_lo_readq_relaxed((iommu)->reg + (addr))
This seems unnecessary (similarly for writeq() below) - once you've
included the header, then it automatically provides readq{_relaxed}()
for RV32 via the non-atomic implementation, and wherever atomicity
doesn't matter, then as written there seems to be no reason for RV64 to
stop using the regular arch readq().
Not that it makes any difference to me either way, but I don't see any
real issue with the spec - if software *may* make a 64-bit access to any
64-bit register unconditionally, then that can only imply that hardware
*must* be able to accommodate RV64 software choosing to do so, and
therefore must support *both* 32b and 64b accesses in general, except
perhaps on RV32-only systems if software could never make 64b accesses
in the first place. If 64b single-copy atomicity is not
required/specified then it should be valid to achieve that by just
sticking a downsizer in the upstream interconnect to split 64b accesses
into 32bx2 incremental bursts.
If do actually you need this as an erratum workaround to support
specific hardware which has misinterpreted the spec then I think you
should be clear about that. Otherwise, adding speculative "workarounds"
which (slightly) penalise hardware that got it right, while inviting
future hardware to get it wrong, doesn't seem like the right way to go
at all...
Thanks,
Robin.
>
> #define riscv_iommu_writel(iommu, addr, val) \
> writel_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_writeq(iommu, addr, val) \
> - writeq_relaxed((val), (iommu)->reg + (addr))
> -
> -#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> - readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> - delay_us, timeout_us)
> + hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
>
> #define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
2026-07-14 12:27 ` Robin Murphy
@ 2026-07-14 13:24 ` Guo Ren
-1 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-07-14 13:24 UTC (permalink / raw)
To: Robin Murphy
Cc: Zhanpeng Zhang, Tomasz Jeznach, Joerg Roedel, Will Deacon,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
David Laight, Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu,
linux-riscv, linux-kernel
On Tue, Jul 14, 2026 at 8:27 PM Robin Murphy <robin.murphy@arm.com> wrote:
>
> On 13/07/2026 1:29 pm, Zhanpeng Zhang wrote:
> > The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
> > using two 32-bit transactions, high half first, and leaves the single-copy
> > atomicity of 8-byte IOMMU register accesses unspecified.
> >
> > Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
> > for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
> > then read the high half and compose the register value from the polled low
> > half. HPM counter reads require a rollover-aware sequence and remain
> > outside these accessors.
> >
> > This follows the 32-bit access direction proposed by Guo Ren [2] and uses
> > the generic non-atomic MMIO helpers suggested by David Laight.
> >
> > [1] https://docs.riscv.org/reference/iommu/
> > [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> >
> > Suggested-by: Guo Ren <guoren@kernel.org>
> > Suggested-by: David Laight <david.laight.linux@gmail.com>
> > Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> > ---
> > Changes in v3:
> > - Use the DDTP access sequence from [1]: retain the low half returned by
> > BUSY polling, read only the high half, and compose the DDTP value from
> > those two 32-bit reads.
> >
> > Changes in v2:
> > - Rework the patch based on Guo Ren's earlier proposal [1].
> > - Drop the build-time option and use 32-bit accesses unconditionally.
> > - Drop the global lock and use the generic high-low MMIO helpers, as
> > suggested by David Laight.
> > - Poll DDTP.BUSY through its low half.
> >
> > Link to v1: [2]
> > Specification discussion: [3]
> >
> > [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> > [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
> > [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
> >
> > drivers/iommu/riscv/iommu.c | 9 ++++++---
> > drivers/iommu/riscv/iommu.h | 9 +++------
> > 2 files changed, 9 insertions(+), 9 deletions(-)
> >
> > diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> > index cec3ddd7ab1..d647b71ebec 100644
> > --- a/drivers/iommu/riscv/iommu.c
> > +++ b/drivers/iommu/riscv/iommu.c
> > @@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
> >
> > #define riscv_iommu_read_ddtp(iommu) ({ \
> > u64 ddtp; \
> > - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> > - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> > + u32 ddtp_lo, ddtp_hi; \
> > + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
> > + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
> > RISCV_IOMMU_DDTP_TIMEOUT); \
> > + ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
>
> It looks like whenever you read DDTP you're only really looking at the
> BUSY/MODE fields anyway, so does this actually need to read the upper
> bits of PPN at all? (The spec says they don't even need to be written on
> RV32 either)
The motivation is not RV32 compatibility. It addresses RV64 platforms
where the IOMMU control plane can only be accessed via 32-bit MMIO.
>
> > + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
> > ddtp; })
> >
> > static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
> > @@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> > * regular boot flow and disable translation when we boot into a kexec
> > * kernel and the previous kernel left them enabled.
> > */
> > - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> > + ddtp = riscv_iommu_read_ddtp(iommu);
> > if (ddtp & RISCV_IOMMU_DDTP_BUSY)
> > return -EBUSY;
> >
> > diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> > index 46df79dd549..1b03790fbe1 100644
> > --- a/drivers/iommu/riscv/iommu.h
> > +++ b/drivers/iommu/riscv/iommu.h
> > @@ -11,6 +11,7 @@
> > #ifndef _RISCV_IOMMU_H_
> > #define _RISCV_IOMMU_H_
> >
> > +#include <linux/io-64-nonatomic-hi-lo.h>
> > #include <linux/iommu.h>
> > #include <linux/types.h>
> > #include <linux/iopoll.h>
> > @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> > readl_relaxed((iommu)->reg + (addr))
> >
> > #define riscv_iommu_readq(iommu, addr) \
> > - readq_relaxed((iommu)->reg + (addr))
> > + hi_lo_readq_relaxed((iommu)->reg + (addr))
>
> This seems unnecessary (similarly for writeq() below) - once you've
> included the header, then it automatically provides readq{_relaxed}()
> for RV32 via the non-atomic implementation, and wherever atomicity
> doesn't matter, then as written there seems to be no reason for RV64 to
> stop using the regular arch readq().
>
> Not that it makes any difference to me either way, but I don't see any
> real issue with the spec - if software *may* make a 64-bit access to any
> 64-bit register unconditionally, then that can only imply that hardware
> *must* be able to accommodate RV64 software choosing to do so, and
> therefore must support *both* 32b and 64b accesses in general, except
> perhaps on RV32-only systems if software could never make 64b accesses
> in the first place. If 64b single-copy atomicity is not
> required/specified then it should be valid to achieve that by just
> sticking a downsizer in the upstream interconnect to split 64b accesses
> into 32bx2 incremental bursts.
>
> If do actually you need this as an erratum workaround to support
> specific hardware which has misinterpreted the spec then I think you
> should be clear about that. Otherwise, adding speculative "workarounds"
> which (slightly) penalise hardware that got it right, while inviting
> future hardware to get it wrong, doesn't seem like the right way to go
> at all...
The only three 64-bit registers the driver ever touches are DDTP,
CAPABILITIES, and MSI_CFG_TBL_ADDR. None of them is on any
performance-critical or hot path. Even if native 64-bit MMIO accesses
were used, there would be no observable performance benefit.
Therefore, this change does not penalize any hardware.
Regarding the specification, there remains substantial disagreement
over whether 64-bit accesses are mandatory. The relevant sentence uses
the word “may” (“may be accessed using either a 32-bit or a 64-bit
access”). Per RFC 2119, “may” does not express a requirement. Because
every existing implementation already provides working 32-bit MMIO
access, we believe the safer and less controversial choice is to use
the portable 32-bit path via the generic hi-lo helpers.
--
Best Regards
Guo Ren
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
@ 2026-07-14 13:24 ` Guo Ren
0 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-07-14 13:24 UTC (permalink / raw)
To: Robin Murphy
Cc: Zhanpeng Zhang, Tomasz Jeznach, Joerg Roedel, Will Deacon,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
David Laight, Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu,
linux-riscv, linux-kernel
On Tue, Jul 14, 2026 at 8:27 PM Robin Murphy <robin.murphy@arm.com> wrote:
>
> On 13/07/2026 1:29 pm, Zhanpeng Zhang wrote:
> > The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
> > using two 32-bit transactions, high half first, and leaves the single-copy
> > atomicity of 8-byte IOMMU register accesses unspecified.
> >
> > Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
> > for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
> > then read the high half and compose the register value from the polled low
> > half. HPM counter reads require a rollover-aware sequence and remain
> > outside these accessors.
> >
> > This follows the 32-bit access direction proposed by Guo Ren [2] and uses
> > the generic non-atomic MMIO helpers suggested by David Laight.
> >
> > [1] https://docs.riscv.org/reference/iommu/
> > [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> >
> > Suggested-by: Guo Ren <guoren@kernel.org>
> > Suggested-by: David Laight <david.laight.linux@gmail.com>
> > Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> > ---
> > Changes in v3:
> > - Use the DDTP access sequence from [1]: retain the low half returned by
> > BUSY polling, read only the high half, and compose the DDTP value from
> > those two 32-bit reads.
> >
> > Changes in v2:
> > - Rework the patch based on Guo Ren's earlier proposal [1].
> > - Drop the build-time option and use 32-bit accesses unconditionally.
> > - Drop the global lock and use the generic high-low MMIO helpers, as
> > suggested by David Laight.
> > - Poll DDTP.BUSY through its low half.
> >
> > Link to v1: [2]
> > Specification discussion: [3]
> >
> > [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> > [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
> > [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
> >
> > drivers/iommu/riscv/iommu.c | 9 ++++++---
> > drivers/iommu/riscv/iommu.h | 9 +++------
> > 2 files changed, 9 insertions(+), 9 deletions(-)
> >
> > diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> > index cec3ddd7ab1..d647b71ebec 100644
> > --- a/drivers/iommu/riscv/iommu.c
> > +++ b/drivers/iommu/riscv/iommu.c
> > @@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
> >
> > #define riscv_iommu_read_ddtp(iommu) ({ \
> > u64 ddtp; \
> > - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> > - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> > + u32 ddtp_lo, ddtp_hi; \
> > + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
> > + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
> > RISCV_IOMMU_DDTP_TIMEOUT); \
> > + ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
>
> It looks like whenever you read DDTP you're only really looking at the
> BUSY/MODE fields anyway, so does this actually need to read the upper
> bits of PPN at all? (The spec says they don't even need to be written on
> RV32 either)
The motivation is not RV32 compatibility. It addresses RV64 platforms
where the IOMMU control plane can only be accessed via 32-bit MMIO.
>
> > + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
> > ddtp; })
> >
> > static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
> > @@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> > * regular boot flow and disable translation when we boot into a kexec
> > * kernel and the previous kernel left them enabled.
> > */
> > - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> > + ddtp = riscv_iommu_read_ddtp(iommu);
> > if (ddtp & RISCV_IOMMU_DDTP_BUSY)
> > return -EBUSY;
> >
> > diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> > index 46df79dd549..1b03790fbe1 100644
> > --- a/drivers/iommu/riscv/iommu.h
> > +++ b/drivers/iommu/riscv/iommu.h
> > @@ -11,6 +11,7 @@
> > #ifndef _RISCV_IOMMU_H_
> > #define _RISCV_IOMMU_H_
> >
> > +#include <linux/io-64-nonatomic-hi-lo.h>
> > #include <linux/iommu.h>
> > #include <linux/types.h>
> > #include <linux/iopoll.h>
> > @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> > readl_relaxed((iommu)->reg + (addr))
> >
> > #define riscv_iommu_readq(iommu, addr) \
> > - readq_relaxed((iommu)->reg + (addr))
> > + hi_lo_readq_relaxed((iommu)->reg + (addr))
>
> This seems unnecessary (similarly for writeq() below) - once you've
> included the header, then it automatically provides readq{_relaxed}()
> for RV32 via the non-atomic implementation, and wherever atomicity
> doesn't matter, then as written there seems to be no reason for RV64 to
> stop using the regular arch readq().
>
> Not that it makes any difference to me either way, but I don't see any
> real issue with the spec - if software *may* make a 64-bit access to any
> 64-bit register unconditionally, then that can only imply that hardware
> *must* be able to accommodate RV64 software choosing to do so, and
> therefore must support *both* 32b and 64b accesses in general, except
> perhaps on RV32-only systems if software could never make 64b accesses
> in the first place. If 64b single-copy atomicity is not
> required/specified then it should be valid to achieve that by just
> sticking a downsizer in the upstream interconnect to split 64b accesses
> into 32bx2 incremental bursts.
>
> If do actually you need this as an erratum workaround to support
> specific hardware which has misinterpreted the spec then I think you
> should be clear about that. Otherwise, adding speculative "workarounds"
> which (slightly) penalise hardware that got it right, while inviting
> future hardware to get it wrong, doesn't seem like the right way to go
> at all...
The only three 64-bit registers the driver ever touches are DDTP,
CAPABILITIES, and MSI_CFG_TBL_ADDR. None of them is on any
performance-critical or hot path. Even if native 64-bit MMIO accesses
were used, there would be no observable performance benefit.
Therefore, this change does not penalize any hardware.
Regarding the specification, there remains substantial disagreement
over whether 64-bit accesses are mandatory. The relevant sentence uses
the word “may” (“may be accessed using either a 32-bit or a 64-bit
access”). Per RFC 2119, “may” does not express a requirement. Because
every existing implementation already provides working 32-bit MMIO
access, we believe the safer and less controversial choice is to use
the portable 32-bit path via the generic hi-lo helpers.
--
Best Regards
Guo Ren
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
2026-07-14 12:27 ` Robin Murphy
@ 2026-07-14 13:55 ` Guo Ren
-1 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-07-14 13:55 UTC (permalink / raw)
To: Robin Murphy
Cc: Zhanpeng Zhang, Tomasz Jeznach, Joerg Roedel, Will Deacon,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
David Laight, Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu,
linux-riscv, linux-kernel
On Tue, Jul 14, 2026 at 8:27 PM Robin Murphy <robin.murphy@arm.com> wrote:
>
> On 13/07/2026 1:29 pm, Zhanpeng Zhang wrote:
> > The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
> > using two 32-bit transactions, high half first, and leaves the single-copy
> > atomicity of 8-byte IOMMU register accesses unspecified.
> >
> > Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
> > for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
> > then read the high half and compose the register value from the polled low
> > half. HPM counter reads require a rollover-aware sequence and remain
> > outside these accessors.
> >
> > This follows the 32-bit access direction proposed by Guo Ren [2] and uses
> > the generic non-atomic MMIO helpers suggested by David Laight.
> >
> > [1] https://docs.riscv.org/reference/iommu/
> > [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> >
> > Suggested-by: Guo Ren <guoren@kernel.org>
> > Suggested-by: David Laight <david.laight.linux@gmail.com>
> > Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> > ---
> > Changes in v3:
> > - Use the DDTP access sequence from [1]: retain the low half returned by
> > BUSY polling, read only the high half, and compose the DDTP value from
> > those two 32-bit reads.
> >
> > Changes in v2:
> > - Rework the patch based on Guo Ren's earlier proposal [1].
> > - Drop the build-time option and use 32-bit accesses unconditionally.
> > - Drop the global lock and use the generic high-low MMIO helpers, as
> > suggested by David Laight.
> > - Poll DDTP.BUSY through its low half.
> >
> > Link to v1: [2]
> > Specification discussion: [3]
> >
> > [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> > [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
> > [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
> >
> > drivers/iommu/riscv/iommu.c | 9 ++++++---
> > drivers/iommu/riscv/iommu.h | 9 +++------
> > 2 files changed, 9 insertions(+), 9 deletions(-)
> >
> > diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> > index cec3ddd7ab1..d647b71ebec 100644
> > --- a/drivers/iommu/riscv/iommu.c
> > +++ b/drivers/iommu/riscv/iommu.c
> > @@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
> >
> > #define riscv_iommu_read_ddtp(iommu) ({ \
> > u64 ddtp; \
> > - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> > - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> > + u32 ddtp_lo, ddtp_hi; \
> > + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
> > + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
> > RISCV_IOMMU_DDTP_TIMEOUT); \
> > + ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
>
> It looks like whenever you read DDTP you're only really looking at the
> BUSY/MODE fields anyway, so does this actually need to read the upper
> bits of PPN at all? (The spec says they don't even need to be written on
> RV32 either)
>
> > + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
> > ddtp; })
> >
> > static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
> > @@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> > * regular boot flow and disable translation when we boot into a kexec
> > * kernel and the previous kernel left them enabled.
> > */
> > - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> > + ddtp = riscv_iommu_read_ddtp(iommu);
> > if (ddtp & RISCV_IOMMU_DDTP_BUSY)
> > return -EBUSY;
> >
> > diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> > index 46df79dd549..1b03790fbe1 100644
> > --- a/drivers/iommu/riscv/iommu.h
> > +++ b/drivers/iommu/riscv/iommu.h
> > @@ -11,6 +11,7 @@
> > #ifndef _RISCV_IOMMU_H_
> > #define _RISCV_IOMMU_H_
> >
> > +#include <linux/io-64-nonatomic-hi-lo.h>
> > #include <linux/iommu.h>
> > #include <linux/types.h>
> > #include <linux/iopoll.h>
> > @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> > readl_relaxed((iommu)->reg + (addr))
> >
> > #define riscv_iommu_readq(iommu, addr) \
> > - readq_relaxed((iommu)->reg + (addr))
> > + hi_lo_readq_relaxed((iommu)->reg + (addr))
>
> This seems unnecessary (similarly for writeq() below) - once you've
> included the header, then it automatically provides readq{_relaxed}()
> for RV32 via the non-atomic implementation, and wherever atomicity
> doesn't matter, then as written there seems to be no reason for RV64 to
> stop using the regular arch readq().
>
> Not that it makes any difference to me either way, but I don't see any
> real issue with the spec - if software *may* make a 64-bit access to any
> 64-bit register unconditionally, then that can only imply that hardware
> *must* be able to accommodate RV64 software choosing to do so, and
> therefore must support *both* 32b and 64b accesses in general, except
> perhaps on RV32-only systems if software could never make 64b accesses
You cannot exclude RV32 from the discussion. The spec statement in question is:
"Registers that are 64-bit wide may be accessed using either a 32-bit
or a 64-bit access."
This sentence applies to all implementations, including RV32 systems.
On a pure RV32 system (or when running RV32 software), 64-bit accesses
are impossible by definition. Therefore, if the spec were interpreted
as making 64-bit access mandatory, the statement itself would become
self-contradictory on RV32 — which is clearly not the intent.
The specification is deliberately precise. It uses "may", not "shall"
or "must". We should not introduce speculative implications that break
the spec's consistency on RV32. The safe and portable interpretation
is that software is explicitly allowed to use 32-bit accesses for
these registers on any system, which is exactly what this patch does.
> in the first place. If 64b single-copy atomicity is not
> required/specified then it should be valid to achieve that by just
> sticking a downsizer in the upstream interconnect to split 64b accesses
> into 32bx2 incremental bursts.
>
> If do actually you need this as an erratum workaround to support
> specific hardware which has misinterpreted the spec then I think you
> should be clear about that. Otherwise, adding speculative "workarounds"
> which (slightly) penalise hardware that got it right, while inviting
> future hardware to get it wrong, doesn't seem like the right way to go
> at all...
>
> Thanks,
> Robin.
>
> >
> > #define riscv_iommu_writel(iommu, addr, val) \
> > writel_relaxed((val), (iommu)->reg + (addr))
> >
> > #define riscv_iommu_writeq(iommu, addr, val) \
> > - writeq_relaxed((val), (iommu)->reg + (addr))
> > -
> > -#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > - readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> > - delay_us, timeout_us)
> > + hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
> >
> > #define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
>
--
Best Regards
Guo Ren
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
@ 2026-07-14 13:55 ` Guo Ren
0 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-07-14 13:55 UTC (permalink / raw)
To: Robin Murphy
Cc: Zhanpeng Zhang, Tomasz Jeznach, Joerg Roedel, Will Deacon,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
David Laight, Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu,
linux-riscv, linux-kernel
On Tue, Jul 14, 2026 at 8:27 PM Robin Murphy <robin.murphy@arm.com> wrote:
>
> On 13/07/2026 1:29 pm, Zhanpeng Zhang wrote:
> > The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
> > using two 32-bit transactions, high half first, and leaves the single-copy
> > atomicity of 8-byte IOMMU register accesses unspecified.
> >
> > Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
> > for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
> > then read the high half and compose the register value from the polled low
> > half. HPM counter reads require a rollover-aware sequence and remain
> > outside these accessors.
> >
> > This follows the 32-bit access direction proposed by Guo Ren [2] and uses
> > the generic non-atomic MMIO helpers suggested by David Laight.
> >
> > [1] https://docs.riscv.org/reference/iommu/
> > [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> >
> > Suggested-by: Guo Ren <guoren@kernel.org>
> > Suggested-by: David Laight <david.laight.linux@gmail.com>
> > Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> > ---
> > Changes in v3:
> > - Use the DDTP access sequence from [1]: retain the low half returned by
> > BUSY polling, read only the high half, and compose the DDTP value from
> > those two 32-bit reads.
> >
> > Changes in v2:
> > - Rework the patch based on Guo Ren's earlier proposal [1].
> > - Drop the build-time option and use 32-bit accesses unconditionally.
> > - Drop the global lock and use the generic high-low MMIO helpers, as
> > suggested by David Laight.
> > - Poll DDTP.BUSY through its low half.
> >
> > Link to v1: [2]
> > Specification discussion: [3]
> >
> > [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> > [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
> > [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
> >
> > drivers/iommu/riscv/iommu.c | 9 ++++++---
> > drivers/iommu/riscv/iommu.h | 9 +++------
> > 2 files changed, 9 insertions(+), 9 deletions(-)
> >
> > diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> > index cec3ddd7ab1..d647b71ebec 100644
> > --- a/drivers/iommu/riscv/iommu.c
> > +++ b/drivers/iommu/riscv/iommu.c
> > @@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
> >
> > #define riscv_iommu_read_ddtp(iommu) ({ \
> > u64 ddtp; \
> > - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> > - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> > + u32 ddtp_lo, ddtp_hi; \
> > + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
> > + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
> > RISCV_IOMMU_DDTP_TIMEOUT); \
> > + ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
>
> It looks like whenever you read DDTP you're only really looking at the
> BUSY/MODE fields anyway, so does this actually need to read the upper
> bits of PPN at all? (The spec says they don't even need to be written on
> RV32 either)
>
> > + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
> > ddtp; })
> >
> > static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
> > @@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> > * regular boot flow and disable translation when we boot into a kexec
> > * kernel and the previous kernel left them enabled.
> > */
> > - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> > + ddtp = riscv_iommu_read_ddtp(iommu);
> > if (ddtp & RISCV_IOMMU_DDTP_BUSY)
> > return -EBUSY;
> >
> > diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> > index 46df79dd549..1b03790fbe1 100644
> > --- a/drivers/iommu/riscv/iommu.h
> > +++ b/drivers/iommu/riscv/iommu.h
> > @@ -11,6 +11,7 @@
> > #ifndef _RISCV_IOMMU_H_
> > #define _RISCV_IOMMU_H_
> >
> > +#include <linux/io-64-nonatomic-hi-lo.h>
> > #include <linux/iommu.h>
> > #include <linux/types.h>
> > #include <linux/iopoll.h>
> > @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> > readl_relaxed((iommu)->reg + (addr))
> >
> > #define riscv_iommu_readq(iommu, addr) \
> > - readq_relaxed((iommu)->reg + (addr))
> > + hi_lo_readq_relaxed((iommu)->reg + (addr))
>
> This seems unnecessary (similarly for writeq() below) - once you've
> included the header, then it automatically provides readq{_relaxed}()
> for RV32 via the non-atomic implementation, and wherever atomicity
> doesn't matter, then as written there seems to be no reason for RV64 to
> stop using the regular arch readq().
>
> Not that it makes any difference to me either way, but I don't see any
> real issue with the spec - if software *may* make a 64-bit access to any
> 64-bit register unconditionally, then that can only imply that hardware
> *must* be able to accommodate RV64 software choosing to do so, and
> therefore must support *both* 32b and 64b accesses in general, except
> perhaps on RV32-only systems if software could never make 64b accesses
You cannot exclude RV32 from the discussion. The spec statement in question is:
"Registers that are 64-bit wide may be accessed using either a 32-bit
or a 64-bit access."
This sentence applies to all implementations, including RV32 systems.
On a pure RV32 system (or when running RV32 software), 64-bit accesses
are impossible by definition. Therefore, if the spec were interpreted
as making 64-bit access mandatory, the statement itself would become
self-contradictory on RV32 — which is clearly not the intent.
The specification is deliberately precise. It uses "may", not "shall"
or "must". We should not introduce speculative implications that break
the spec's consistency on RV32. The safe and portable interpretation
is that software is explicitly allowed to use 32-bit accesses for
these registers on any system, which is exactly what this patch does.
> in the first place. If 64b single-copy atomicity is not
> required/specified then it should be valid to achieve that by just
> sticking a downsizer in the upstream interconnect to split 64b accesses
> into 32bx2 incremental bursts.
>
> If do actually you need this as an erratum workaround to support
> specific hardware which has misinterpreted the spec then I think you
> should be clear about that. Otherwise, adding speculative "workarounds"
> which (slightly) penalise hardware that got it right, while inviting
> future hardware to get it wrong, doesn't seem like the right way to go
> at all...
>
> Thanks,
> Robin.
>
> >
> > #define riscv_iommu_writel(iommu, addr, val) \
> > writel_relaxed((val), (iommu)->reg + (addr))
> >
> > #define riscv_iommu_writeq(iommu, addr, val) \
> > - writeq_relaxed((val), (iommu)->reg + (addr))
> > -
> > -#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > - readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
> > - delay_us, timeout_us)
> > + hi_lo_writeq_relaxed((val), (iommu)->reg + (addr))
> >
> > #define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
> > readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
>
--
Best Regards
Guo Ren
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
2026-07-14 13:24 ` Guo Ren
@ 2026-07-14 14:53 ` David Laight
-1 siblings, 0 replies; 70+ messages in thread
From: David Laight @ 2026-07-14 14:53 UTC (permalink / raw)
To: Guo Ren
Cc: Robin Murphy, Zhanpeng Zhang, Tomasz Jeznach, Joerg Roedel,
Will Deacon, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu,
linux-riscv, linux-kernel
On Tue, 14 Jul 2026 21:24:19 +0800
Guo Ren <guoren@kernel.org> wrote:
> On Tue, Jul 14, 2026 at 8:27 PM Robin Murphy <robin.murphy@arm.com> wrote:
> >
> > On 13/07/2026 1:29 pm, Zhanpeng Zhang wrote:
> > > The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
> > > using two 32-bit transactions, high half first, and leaves the single-copy
> > > atomicity of 8-byte IOMMU register accesses unspecified.
> > >
> > > Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
> > > for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
> > > then read the high half and compose the register value from the polled low
> > > half. HPM counter reads require a rollover-aware sequence and remain
> > > outside these accessors.
> > >
> > > This follows the 32-bit access direction proposed by Guo Ren [2] and uses
> > > the generic non-atomic MMIO helpers suggested by David Laight.
> > >
> > > [1] https://docs.riscv.org/reference/iommu/
> > > [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> > >
> > > Suggested-by: Guo Ren <guoren@kernel.org>
> > > Suggested-by: David Laight <david.laight.linux@gmail.com>
> > > Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> > > ---
> > > Changes in v3:
> > > - Use the DDTP access sequence from [1]: retain the low half returned by
> > > BUSY polling, read only the high half, and compose the DDTP value from
> > > those two 32-bit reads.
> > >
> > > Changes in v2:
> > > - Rework the patch based on Guo Ren's earlier proposal [1].
> > > - Drop the build-time option and use 32-bit accesses unconditionally.
> > > - Drop the global lock and use the generic high-low MMIO helpers, as
> > > suggested by David Laight.
> > > - Poll DDTP.BUSY through its low half.
> > >
> > > Link to v1: [2]
> > > Specification discussion: [3]
> > >
> > > [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> > > [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
> > > [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
> > >
> > > drivers/iommu/riscv/iommu.c | 9 ++++++---
> > > drivers/iommu/riscv/iommu.h | 9 +++------
> > > 2 files changed, 9 insertions(+), 9 deletions(-)
> > >
> > > diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> > > index cec3ddd7ab1..d647b71ebec 100644
> > > --- a/drivers/iommu/riscv/iommu.c
> > > +++ b/drivers/iommu/riscv/iommu.c
> > > @@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
> > >
> > > #define riscv_iommu_read_ddtp(iommu) ({ \
> > > u64 ddtp; \
> > > - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> > > - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> > > + u32 ddtp_lo, ddtp_hi; \
> > > + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
> > > + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
> > > RISCV_IOMMU_DDTP_TIMEOUT); \
> > > + ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
> >
> > It looks like whenever you read DDTP you're only really looking at the
> > BUSY/MODE fields anyway, so does this actually need to read the upper
> > bits of PPN at all? (The spec says they don't even need to be written on
> > RV32 either)
>
> The motivation is not RV32 compatibility. It addresses RV64 platforms
> where the IOMMU control plane can only be accessed via 32-bit MMIO.
>
> >
> > > + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
> > > ddtp; })
> > >
> > > static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
> > > @@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> > > * regular boot flow and disable translation when we boot into a kexec
> > > * kernel and the previous kernel left them enabled.
> > > */
> > > - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> > > + ddtp = riscv_iommu_read_ddtp(iommu);
> > > if (ddtp & RISCV_IOMMU_DDTP_BUSY)
> > > return -EBUSY;
> > >
> > > diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> > > index 46df79dd549..1b03790fbe1 100644
> > > --- a/drivers/iommu/riscv/iommu.h
> > > +++ b/drivers/iommu/riscv/iommu.h
> > > @@ -11,6 +11,7 @@
> > > #ifndef _RISCV_IOMMU_H_
> > > #define _RISCV_IOMMU_H_
> > >
> > > +#include <linux/io-64-nonatomic-hi-lo.h>
> > > #include <linux/iommu.h>
> > > #include <linux/types.h>
> > > #include <linux/iopoll.h>
> > > @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> > > readl_relaxed((iommu)->reg + (addr))
> > >
> > > #define riscv_iommu_readq(iommu, addr) \
> > > - readq_relaxed((iommu)->reg + (addr))
> > > + hi_lo_readq_relaxed((iommu)->reg + (addr))
> >
> > This seems unnecessary (similarly for writeq() below) - once you've
> > included the header, then it automatically provides readq{_relaxed}()
> > for RV32 via the non-atomic implementation, and wherever atomicity
> > doesn't matter, then as written there seems to be no reason for RV64 to
> > stop using the regular arch readq().
> >
> > Not that it makes any difference to me either way, but I don't see any
> > real issue with the spec - if software *may* make a 64-bit access to any
> > 64-bit register unconditionally, then that can only imply that hardware
> > *must* be able to accommodate RV64 software choosing to do so, and
> > therefore must support *both* 32b and 64b accesses in general, except
> > perhaps on RV32-only systems if software could never make 64b accesses
> > in the first place. If 64b single-copy atomicity is not
> > required/specified then it should be valid to achieve that by just
> > sticking a downsizer in the upstream interconnect to split 64b accesses
> > into 32bx2 incremental bursts.
> >
> > If do actually you need this as an erratum workaround to support
> > specific hardware which has misinterpreted the spec then I think you
> > should be clear about that. Otherwise, adding speculative "workarounds"
> > which (slightly) penalise hardware that got it right, while inviting
> > future hardware to get it wrong, doesn't seem like the right way to go
> > at all...
>
> The only three 64-bit registers the driver ever touches are DDTP,
> CAPABILITIES, and MSI_CFG_TBL_ADDR. None of them is on any
> performance-critical or hot path. Even if native 64-bit MMIO accesses
> were used, there would be no observable performance benefit.
> Therefore, this change does not penalize any hardware.
>
> Regarding the specification, there remains substantial disagreement
> over whether 64-bit accesses are mandatory. The relevant sentence uses
> the word “may” (“may be accessed using either a 32-bit or a 64-bit
> access”). Per RFC 2119, “may” does not express a requirement. Because
> every existing implementation already provides working 32-bit MMIO
> access, we believe the safer and less controversial choice is to use
> the portable 32-bit path via the generic hi-lo helpers.
>
There is also this clause:
The 8-byte IOMMU registers are defined in such a way that software can
perform two individual 4-byte accesses, or hardware can perform two
independent 4-byte transactions resulting from an 8-byte access,
to the high and low halves of the register, in that order, as long as
the register semantics, with regard to side-effects, are respected
between the two software accesses, or two hardware transactions,
respectively.
I'm not sure what the relevance of the high-low order is.
It could mean, for example, that a read of the high word can latch the low
for the next read - so that you get an atomic 64bit read.
That might be ok if hardware splits the 64bit read, but if software does it
it isn't going to go well unless the driver locks all (and it might be all)
slave accesses.
In the worst case there might be a single latch for all odd word reads.
Writes could be worse!
Hardware can be that broken.
I remember a PCI slave that terminated reads requesting 'cycle rerun'
and then assumed that the following reads would be repeats of the same
read, not reads of a different address by the other cpu.
It didn't do writes properly either, I've forgotten the exact details
but I think a second (third?) write overwrite some latches.
David
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
@ 2026-07-14 14:53 ` David Laight
0 siblings, 0 replies; 70+ messages in thread
From: David Laight @ 2026-07-14 14:53 UTC (permalink / raw)
To: Guo Ren
Cc: Robin Murphy, Zhanpeng Zhang, Tomasz Jeznach, Joerg Roedel,
Will Deacon, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu,
linux-riscv, linux-kernel
On Tue, 14 Jul 2026 21:24:19 +0800
Guo Ren <guoren@kernel.org> wrote:
> On Tue, Jul 14, 2026 at 8:27 PM Robin Murphy <robin.murphy@arm.com> wrote:
> >
> > On 13/07/2026 1:29 pm, Zhanpeng Zhang wrote:
> > > The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
> > > using two 32-bit transactions, high half first, and leaves the single-copy
> > > atomicity of 8-byte IOMMU register accesses unspecified.
> > >
> > > Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
> > > for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
> > > then read the high half and compose the register value from the polled low
> > > half. HPM counter reads require a rollover-aware sequence and remain
> > > outside these accessors.
> > >
> > > This follows the 32-bit access direction proposed by Guo Ren [2] and uses
> > > the generic non-atomic MMIO helpers suggested by David Laight.
> > >
> > > [1] https://docs.riscv.org/reference/iommu/
> > > [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> > >
> > > Suggested-by: Guo Ren <guoren@kernel.org>
> > > Suggested-by: David Laight <david.laight.linux@gmail.com>
> > > Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> > > ---
> > > Changes in v3:
> > > - Use the DDTP access sequence from [1]: retain the low half returned by
> > > BUSY polling, read only the high half, and compose the DDTP value from
> > > those two 32-bit reads.
> > >
> > > Changes in v2:
> > > - Rework the patch based on Guo Ren's earlier proposal [1].
> > > - Drop the build-time option and use 32-bit accesses unconditionally.
> > > - Drop the global lock and use the generic high-low MMIO helpers, as
> > > suggested by David Laight.
> > > - Poll DDTP.BUSY through its low half.
> > >
> > > Link to v1: [2]
> > > Specification discussion: [3]
> > >
> > > [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> > > [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
> > > [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
> > >
> > > drivers/iommu/riscv/iommu.c | 9 ++++++---
> > > drivers/iommu/riscv/iommu.h | 9 +++------
> > > 2 files changed, 9 insertions(+), 9 deletions(-)
> > >
> > > diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> > > index cec3ddd7ab1..d647b71ebec 100644
> > > --- a/drivers/iommu/riscv/iommu.c
> > > +++ b/drivers/iommu/riscv/iommu.c
> > > @@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
> > >
> > > #define riscv_iommu_read_ddtp(iommu) ({ \
> > > u64 ddtp; \
> > > - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> > > - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> > > + u32 ddtp_lo, ddtp_hi; \
> > > + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
> > > + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
> > > RISCV_IOMMU_DDTP_TIMEOUT); \
> > > + ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
> >
> > It looks like whenever you read DDTP you're only really looking at the
> > BUSY/MODE fields anyway, so does this actually need to read the upper
> > bits of PPN at all? (The spec says they don't even need to be written on
> > RV32 either)
>
> The motivation is not RV32 compatibility. It addresses RV64 platforms
> where the IOMMU control plane can only be accessed via 32-bit MMIO.
>
> >
> > > + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
> > > ddtp; })
> > >
> > > static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
> > > @@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> > > * regular boot flow and disable translation when we boot into a kexec
> > > * kernel and the previous kernel left them enabled.
> > > */
> > > - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> > > + ddtp = riscv_iommu_read_ddtp(iommu);
> > > if (ddtp & RISCV_IOMMU_DDTP_BUSY)
> > > return -EBUSY;
> > >
> > > diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> > > index 46df79dd549..1b03790fbe1 100644
> > > --- a/drivers/iommu/riscv/iommu.h
> > > +++ b/drivers/iommu/riscv/iommu.h
> > > @@ -11,6 +11,7 @@
> > > #ifndef _RISCV_IOMMU_H_
> > > #define _RISCV_IOMMU_H_
> > >
> > > +#include <linux/io-64-nonatomic-hi-lo.h>
> > > #include <linux/iommu.h>
> > > #include <linux/types.h>
> > > #include <linux/iopoll.h>
> > > @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> > > readl_relaxed((iommu)->reg + (addr))
> > >
> > > #define riscv_iommu_readq(iommu, addr) \
> > > - readq_relaxed((iommu)->reg + (addr))
> > > + hi_lo_readq_relaxed((iommu)->reg + (addr))
> >
> > This seems unnecessary (similarly for writeq() below) - once you've
> > included the header, then it automatically provides readq{_relaxed}()
> > for RV32 via the non-atomic implementation, and wherever atomicity
> > doesn't matter, then as written there seems to be no reason for RV64 to
> > stop using the regular arch readq().
> >
> > Not that it makes any difference to me either way, but I don't see any
> > real issue with the spec - if software *may* make a 64-bit access to any
> > 64-bit register unconditionally, then that can only imply that hardware
> > *must* be able to accommodate RV64 software choosing to do so, and
> > therefore must support *both* 32b and 64b accesses in general, except
> > perhaps on RV32-only systems if software could never make 64b accesses
> > in the first place. If 64b single-copy atomicity is not
> > required/specified then it should be valid to achieve that by just
> > sticking a downsizer in the upstream interconnect to split 64b accesses
> > into 32bx2 incremental bursts.
> >
> > If do actually you need this as an erratum workaround to support
> > specific hardware which has misinterpreted the spec then I think you
> > should be clear about that. Otherwise, adding speculative "workarounds"
> > which (slightly) penalise hardware that got it right, while inviting
> > future hardware to get it wrong, doesn't seem like the right way to go
> > at all...
>
> The only three 64-bit registers the driver ever touches are DDTP,
> CAPABILITIES, and MSI_CFG_TBL_ADDR. None of them is on any
> performance-critical or hot path. Even if native 64-bit MMIO accesses
> were used, there would be no observable performance benefit.
> Therefore, this change does not penalize any hardware.
>
> Regarding the specification, there remains substantial disagreement
> over whether 64-bit accesses are mandatory. The relevant sentence uses
> the word “may” (“may be accessed using either a 32-bit or a 64-bit
> access”). Per RFC 2119, “may” does not express a requirement. Because
> every existing implementation already provides working 32-bit MMIO
> access, we believe the safer and less controversial choice is to use
> the portable 32-bit path via the generic hi-lo helpers.
>
There is also this clause:
The 8-byte IOMMU registers are defined in such a way that software can
perform two individual 4-byte accesses, or hardware can perform two
independent 4-byte transactions resulting from an 8-byte access,
to the high and low halves of the register, in that order, as long as
the register semantics, with regard to side-effects, are respected
between the two software accesses, or two hardware transactions,
respectively.
I'm not sure what the relevance of the high-low order is.
It could mean, for example, that a read of the high word can latch the low
for the next read - so that you get an atomic 64bit read.
That might be ok if hardware splits the 64bit read, but if software does it
it isn't going to go well unless the driver locks all (and it might be all)
slave accesses.
In the worst case there might be a single latch for all odd word reads.
Writes could be worse!
Hardware can be that broken.
I remember a PCI slave that terminated reads requesting 'cycle rerun'
and then assumed that the following reads would be repeats of the same
read, not reads of a different address by the other cpu.
It didn't do writes properly either, I've forgotten the exact details
but I think a second (third?) write overwrite some latches.
David
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
2026-07-14 14:53 ` David Laight
@ 2026-07-14 16:53 ` Guo Ren
-1 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-07-14 16:53 UTC (permalink / raw)
To: David Laight
Cc: Robin Murphy, Zhanpeng Zhang, Tomasz Jeznach, Joerg Roedel,
Will Deacon, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu,
linux-riscv, linux-kernel
On Tue, Jul 14, 2026 at 10:53 PM David Laight
<david.laight.linux@gmail.com> wrote:
>
> On Tue, 14 Jul 2026 21:24:19 +0800
> Guo Ren <guoren@kernel.org> wrote:
>
> > On Tue, Jul 14, 2026 at 8:27 PM Robin Murphy <robin.murphy@arm.com> wrote:
> > >
> > > On 13/07/2026 1:29 pm, Zhanpeng Zhang wrote:
> > > > The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
> > > > using two 32-bit transactions, high half first, and leaves the single-copy
> > > > atomicity of 8-byte IOMMU register accesses unspecified.
> > > >
> > > > Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
> > > > for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
> > > > then read the high half and compose the register value from the polled low
> > > > half. HPM counter reads require a rollover-aware sequence and remain
> > > > outside these accessors.
> > > >
> > > > This follows the 32-bit access direction proposed by Guo Ren [2] and uses
> > > > the generic non-atomic MMIO helpers suggested by David Laight.
> > > >
> > > > [1] https://docs.riscv.org/reference/iommu/
> > > > [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> > > >
> > > > Suggested-by: Guo Ren <guoren@kernel.org>
> > > > Suggested-by: David Laight <david.laight.linux@gmail.com>
> > > > Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> > > > ---
> > > > Changes in v3:
> > > > - Use the DDTP access sequence from [1]: retain the low half returned by
> > > > BUSY polling, read only the high half, and compose the DDTP value from
> > > > those two 32-bit reads.
> > > >
> > > > Changes in v2:
> > > > - Rework the patch based on Guo Ren's earlier proposal [1].
> > > > - Drop the build-time option and use 32-bit accesses unconditionally.
> > > > - Drop the global lock and use the generic high-low MMIO helpers, as
> > > > suggested by David Laight.
> > > > - Poll DDTP.BUSY through its low half.
> > > >
> > > > Link to v1: [2]
> > > > Specification discussion: [3]
> > > >
> > > > [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> > > > [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
> > > > [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
> > > >
> > > > drivers/iommu/riscv/iommu.c | 9 ++++++---
> > > > drivers/iommu/riscv/iommu.h | 9 +++------
> > > > 2 files changed, 9 insertions(+), 9 deletions(-)
> > > >
> > > > diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> > > > index cec3ddd7ab1..d647b71ebec 100644
> > > > --- a/drivers/iommu/riscv/iommu.c
> > > > +++ b/drivers/iommu/riscv/iommu.c
> > > > @@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
> > > >
> > > > #define riscv_iommu_read_ddtp(iommu) ({ \
> > > > u64 ddtp; \
> > > > - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> > > > - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> > > > + u32 ddtp_lo, ddtp_hi; \
> > > > + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
> > > > + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
> > > > RISCV_IOMMU_DDTP_TIMEOUT); \
> > > > + ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
> > >
> > > It looks like whenever you read DDTP you're only really looking at the
> > > BUSY/MODE fields anyway, so does this actually need to read the upper
> > > bits of PPN at all? (The spec says they don't even need to be written on
> > > RV32 either)
> >
> > The motivation is not RV32 compatibility. It addresses RV64 platforms
> > where the IOMMU control plane can only be accessed via 32-bit MMIO.
> >
> > >
> > > > + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
> > > > ddtp; })
> > > >
> > > > static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
> > > > @@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> > > > * regular boot flow and disable translation when we boot into a kexec
> > > > * kernel and the previous kernel left them enabled.
> > > > */
> > > > - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> > > > + ddtp = riscv_iommu_read_ddtp(iommu);
> > > > if (ddtp & RISCV_IOMMU_DDTP_BUSY)
> > > > return -EBUSY;
> > > >
> > > > diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> > > > index 46df79dd549..1b03790fbe1 100644
> > > > --- a/drivers/iommu/riscv/iommu.h
> > > > +++ b/drivers/iommu/riscv/iommu.h
> > > > @@ -11,6 +11,7 @@
> > > > #ifndef _RISCV_IOMMU_H_
> > > > #define _RISCV_IOMMU_H_
> > > >
> > > > +#include <linux/io-64-nonatomic-hi-lo.h>
> > > > #include <linux/iommu.h>
> > > > #include <linux/types.h>
> > > > #include <linux/iopoll.h>
> > > > @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> > > > readl_relaxed((iommu)->reg + (addr))
> > > >
> > > > #define riscv_iommu_readq(iommu, addr) \
> > > > - readq_relaxed((iommu)->reg + (addr))
> > > > + hi_lo_readq_relaxed((iommu)->reg + (addr))
> > >
> > > This seems unnecessary (similarly for writeq() below) - once you've
> > > included the header, then it automatically provides readq{_relaxed}()
> > > for RV32 via the non-atomic implementation, and wherever atomicity
> > > doesn't matter, then as written there seems to be no reason for RV64 to
> > > stop using the regular arch readq().
> > >
> > > Not that it makes any difference to me either way, but I don't see any
> > > real issue with the spec - if software *may* make a 64-bit access to any
> > > 64-bit register unconditionally, then that can only imply that hardware
> > > *must* be able to accommodate RV64 software choosing to do so, and
> > > therefore must support *both* 32b and 64b accesses in general, except
> > > perhaps on RV32-only systems if software could never make 64b accesses
> > > in the first place. If 64b single-copy atomicity is not
> > > required/specified then it should be valid to achieve that by just
> > > sticking a downsizer in the upstream interconnect to split 64b accesses
> > > into 32bx2 incremental bursts.
> > >
> > > If do actually you need this as an erratum workaround to support
> > > specific hardware which has misinterpreted the spec then I think you
> > > should be clear about that. Otherwise, adding speculative "workarounds"
> > > which (slightly) penalise hardware that got it right, while inviting
> > > future hardware to get it wrong, doesn't seem like the right way to go
> > > at all...
> >
> > The only three 64-bit registers the driver ever touches are DDTP,
> > CAPABILITIES, and MSI_CFG_TBL_ADDR. None of them is on any
> > performance-critical or hot path. Even if native 64-bit MMIO accesses
> > were used, there would be no observable performance benefit.
> > Therefore, this change does not penalize any hardware.
> >
> > Regarding the specification, there remains substantial disagreement
> > over whether 64-bit accesses are mandatory. The relevant sentence uses
> > the word “may” (“may be accessed using either a 32-bit or a 64-bit
> > access”). Per RFC 2119, “may” does not express a requirement. Because
> > every existing implementation already provides working 32-bit MMIO
> > access, we believe the safer and less controversial choice is to use
> > the portable 32-bit path via the generic hi-lo helpers.
> >
>
> There is also this clause:
> The 8-byte IOMMU registers are defined in such a way that software can
> perform two individual 4-byte accesses, or hardware can perform two
> independent 4-byte transactions resulting from an 8-byte access,
> to the high and low halves of the register, in that order, as long as
> the register semantics, with regard to side-effects, are respected
> between the two software accesses, or two hardware transactions,
> respectively.
>
> I'm not sure what the relevance of the high-low order is.
> It could mean, for example, that a read of the high word can latch the low
> for the next read - so that you get an atomic 64bit read.
> That might be ok if hardware splits the 64bit read, but if software does it
> it isn't going to go well unless the driver locks all (and it might be all)
> slave accesses.
> In the worst case there might be a single latch for all odd word reads.
> Writes could be worse!
>
> Hardware can be that broken.
> I remember a PCI slave that terminated reads requesting 'cycle rerun'
> and then assumed that the following reads would be repeats of the same
> read, not reads of a different address by the other cpu.
> It didn't do writes properly either, I've forgotten the exact details
> but I think a second (third?) write overwrite some latches.
This is precisely why the specification leaves the single-copy
atomicity of an 8-byte IOMMU register access UNSPECIFIED. If a 64-bit
MMIO register were implemented with atomic semantics, 32-bit accesses
using lw/sw might require implicit mechanisms, such as latching half
the register for subsequent accesses. Such mechanisms could lead to
exactly the kind of interleaving problem you described.
This is one reason hardware may favor straightforward, latch-free
32-bit MMIO transactions. The example you mentioned further
illustrates the robustness of treating each 32-bit MMIO access as an
independent transaction without relying on hidden latch semantics.
Using 32-bit MMIO accesses for DDTP, CAPABILITIES, and
MSI_CFG_TBL_ADDR in this driver introduces no meaningful performance
overhead and provides broader compatibility without disadvantaging any
RISC-V IOMMU implementation.
--
Best Regards
Guo Ren
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v3] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers
@ 2026-07-14 16:53 ` Guo Ren
0 siblings, 0 replies; 70+ messages in thread
From: Guo Ren @ 2026-07-14 16:53 UTC (permalink / raw)
To: David Laight
Cc: Robin Murphy, Zhanpeng Zhang, Tomasz Jeznach, Joerg Roedel,
Will Deacon, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Vivian Wang, Zong Li, cuiyunhui, yuanzhu, iommu,
linux-riscv, linux-kernel
On Tue, Jul 14, 2026 at 10:53 PM David Laight
<david.laight.linux@gmail.com> wrote:
>
> On Tue, 14 Jul 2026 21:24:19 +0800
> Guo Ren <guoren@kernel.org> wrote:
>
> > On Tue, Jul 14, 2026 at 8:27 PM Robin Murphy <robin.murphy@arm.com> wrote:
> > >
> > > On 13/07/2026 1:29 pm, Zhanpeng Zhang wrote:
> > > > The RISC-V IOMMU specification [1] permits 64-bit registers to be accessed
> > > > using two 32-bit transactions, high half first, and leaves the single-copy
> > > > atomicity of 8-byte IOMMU register accesses unspecified.
> > > >
> > > > Use the generic hi_lo_readq_relaxed() and hi_lo_writeq_relaxed() helpers
> > > > for ordinary 64-bit IOMMU registers. For DDTP, poll BUSY in the low half,
> > > > then read the high half and compose the register value from the polled low
> > > > half. HPM counter reads require a rollover-aware sequence and remain
> > > > outside these accessors.
> > > >
> > > > This follows the 32-bit access direction proposed by Guo Ren [2] and uses
> > > > the generic non-atomic MMIO helpers suggested by David Laight.
> > > >
> > > > [1] https://docs.riscv.org/reference/iommu/
> > > > [2] https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> > > >
> > > > Suggested-by: Guo Ren <guoren@kernel.org>
> > > > Suggested-by: David Laight <david.laight.linux@gmail.com>
> > > > Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
> > > > ---
> > > > Changes in v3:
> > > > - Use the DDTP access sequence from [1]: retain the low half returned by
> > > > BUSY polling, read only the high half, and compose the DDTP value from
> > > > those two 32-bit reads.
> > > >
> > > > Changes in v2:
> > > > - Rework the patch based on Guo Ren's earlier proposal [1].
> > > > - Drop the build-time option and use 32-bit accesses unconditionally.
> > > > - Drop the global lock and use the generic high-low MMIO helpers, as
> > > > suggested by David Laight.
> > > > - Poll DDTP.BUSY through its low half.
> > > >
> > > > Link to v1: [2]
> > > > Specification discussion: [3]
> > > >
> > > > [1]: https://lore.kernel.org/r/20250903144217.837448-1-guoren@kernel.org
> > > > [2]: https://lore.kernel.org/r/20260615064855.90316-1-zhangzhanpeng.jasper@bytedance.com
> > > > [3]: https://github.com/riscv-non-isa/riscv-iommu/issues/765
> > > >
> > > > drivers/iommu/riscv/iommu.c | 9 ++++++---
> > > > drivers/iommu/riscv/iommu.h | 9 +++------
> > > > 2 files changed, 9 insertions(+), 9 deletions(-)
> > > >
> > > > diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> > > > index cec3ddd7ab1..d647b71ebec 100644
> > > > --- a/drivers/iommu/riscv/iommu.c
> > > > +++ b/drivers/iommu/riscv/iommu.c
> > > > @@ -670,9 +670,12 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu)
> > > >
> > > > #define riscv_iommu_read_ddtp(iommu) ({ \
> > > > u64 ddtp; \
> > > > - riscv_iommu_readq_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp, \
> > > > - !(ddtp & RISCV_IOMMU_DDTP_BUSY), 10, \
> > > > + u32 ddtp_lo, ddtp_hi; \
> > > > + riscv_iommu_readl_timeout((iommu), RISCV_IOMMU_REG_DDTP, ddtp_lo, \
> > > > + !(ddtp_lo & RISCV_IOMMU_DDTP_BUSY), 10, \
> > > > RISCV_IOMMU_DDTP_TIMEOUT); \
> > > > + ddtp_hi = riscv_iommu_readl((iommu), RISCV_IOMMU_REG_DDTP + 4); \
> > >
> > > It looks like whenever you read DDTP you're only really looking at the
> > > BUSY/MODE fields anyway, so does this actually need to read the upper
> > > bits of PPN at all? (The spec says they don't even need to be written on
> > > RV32 either)
> >
> > The motivation is not RV32 compatibility. It addresses RV64 platforms
> > where the IOMMU control plane can only be accessed via 32-bit MMIO.
> >
> > >
> > > > + ddtp = ((u64)ddtp_hi << 32) | ddtp_lo; \
> > > > ddtp; })
> > > >
> > > > static int riscv_iommu_iodir_alloc(struct riscv_iommu_device *iommu)
> > > > @@ -1501,7 +1504,7 @@ static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
> > > > * regular boot flow and disable translation when we boot into a kexec
> > > > * kernel and the previous kernel left them enabled.
> > > > */
> > > > - ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
> > > > + ddtp = riscv_iommu_read_ddtp(iommu);
> > > > if (ddtp & RISCV_IOMMU_DDTP_BUSY)
> > > > return -EBUSY;
> > > >
> > > > diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> > > > index 46df79dd549..1b03790fbe1 100644
> > > > --- a/drivers/iommu/riscv/iommu.h
> > > > +++ b/drivers/iommu/riscv/iommu.h
> > > > @@ -11,6 +11,7 @@
> > > > #ifndef _RISCV_IOMMU_H_
> > > > #define _RISCV_IOMMU_H_
> > > >
> > > > +#include <linux/io-64-nonatomic-hi-lo.h>
> > > > #include <linux/iommu.h>
> > > > #include <linux/types.h>
> > > > #include <linux/iopoll.h>
> > > > @@ -70,17 +71,13 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
> > > > readl_relaxed((iommu)->reg + (addr))
> > > >
> > > > #define riscv_iommu_readq(iommu, addr) \
> > > > - readq_relaxed((iommu)->reg + (addr))
> > > > + hi_lo_readq_relaxed((iommu)->reg + (addr))
> > >
> > > This seems unnecessary (similarly for writeq() below) - once you've
> > > included the header, then it automatically provides readq{_relaxed}()
> > > for RV32 via the non-atomic implementation, and wherever atomicity
> > > doesn't matter, then as written there seems to be no reason for RV64 to
> > > stop using the regular arch readq().
> > >
> > > Not that it makes any difference to me either way, but I don't see any
> > > real issue with the spec - if software *may* make a 64-bit access to any
> > > 64-bit register unconditionally, then that can only imply that hardware
> > > *must* be able to accommodate RV64 software choosing to do so, and
> > > therefore must support *both* 32b and 64b accesses in general, except
> > > perhaps on RV32-only systems if software could never make 64b accesses
> > > in the first place. If 64b single-copy atomicity is not
> > > required/specified then it should be valid to achieve that by just
> > > sticking a downsizer in the upstream interconnect to split 64b accesses
> > > into 32bx2 incremental bursts.
> > >
> > > If do actually you need this as an erratum workaround to support
> > > specific hardware which has misinterpreted the spec then I think you
> > > should be clear about that. Otherwise, adding speculative "workarounds"
> > > which (slightly) penalise hardware that got it right, while inviting
> > > future hardware to get it wrong, doesn't seem like the right way to go
> > > at all...
> >
> > The only three 64-bit registers the driver ever touches are DDTP,
> > CAPABILITIES, and MSI_CFG_TBL_ADDR. None of them is on any
> > performance-critical or hot path. Even if native 64-bit MMIO accesses
> > were used, there would be no observable performance benefit.
> > Therefore, this change does not penalize any hardware.
> >
> > Regarding the specification, there remains substantial disagreement
> > over whether 64-bit accesses are mandatory. The relevant sentence uses
> > the word “may” (“may be accessed using either a 32-bit or a 64-bit
> > access”). Per RFC 2119, “may” does not express a requirement. Because
> > every existing implementation already provides working 32-bit MMIO
> > access, we believe the safer and less controversial choice is to use
> > the portable 32-bit path via the generic hi-lo helpers.
> >
>
> There is also this clause:
> The 8-byte IOMMU registers are defined in such a way that software can
> perform two individual 4-byte accesses, or hardware can perform two
> independent 4-byte transactions resulting from an 8-byte access,
> to the high and low halves of the register, in that order, as long as
> the register semantics, with regard to side-effects, are respected
> between the two software accesses, or two hardware transactions,
> respectively.
>
> I'm not sure what the relevance of the high-low order is.
> It could mean, for example, that a read of the high word can latch the low
> for the next read - so that you get an atomic 64bit read.
> That might be ok if hardware splits the 64bit read, but if software does it
> it isn't going to go well unless the driver locks all (and it might be all)
> slave accesses.
> In the worst case there might be a single latch for all odd word reads.
> Writes could be worse!
>
> Hardware can be that broken.
> I remember a PCI slave that terminated reads requesting 'cycle rerun'
> and then assumed that the following reads would be repeats of the same
> read, not reads of a different address by the other cpu.
> It didn't do writes properly either, I've forgotten the exact details
> but I think a second (third?) write overwrite some latches.
This is precisely why the specification leaves the single-copy
atomicity of an 8-byte IOMMU register access UNSPECIFIED. If a 64-bit
MMIO register were implemented with atomic semantics, 32-bit accesses
using lw/sw might require implicit mechanisms, such as latching half
the register for subsequent accesses. Such mechanisms could lead to
exactly the kind of interleaving problem you described.
This is one reason hardware may favor straightforward, latch-free
32-bit MMIO transactions. The example you mentioned further
illustrates the robustness of treating each 32-bit MMIO access as an
independent transaction without relying on hidden latch semantics.
Using 32-bit MMIO accesses for DDTP, CAPABILITIES, and
MSI_CFG_TBL_ADDR in this driver introduces no meaningful performance
overhead and provides broader compatibility without disadvantaging any
RISC-V IOMMU implementation.
--
Best Regards
Guo Ren
^ permalink raw reply [flat|nested] 70+ messages in thread
end of thread, other threads:[~2026-07-14 16:53 UTC | newest]
Thread overview: 70+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-15 6:48 [PATCH v1] iommu/riscv: Support 32-bit register accesses Zhanpeng Zhang
2026-06-15 6:48 ` Zhanpeng Zhang
2026-06-15 8:21 ` Andreas Schwab
2026-06-15 8:21 ` Andreas Schwab
2026-06-15 9:51 ` [External] " Zhanpeng Zhang
2026-06-15 9:51 ` Zhanpeng Zhang
2026-06-15 9:59 ` David Laight
2026-06-15 9:59 ` David Laight
2026-06-15 13:21 ` [External] " Zhanpeng Zhang
2026-06-15 13:21 ` Zhanpeng Zhang
2026-06-15 12:38 ` Guo Ren
2026-06-15 12:38 ` Guo Ren
2026-06-15 13:23 ` [External] " Zhanpeng Zhang
2026-06-15 13:23 ` Zhanpeng Zhang
2026-06-16 10:36 ` David Laight
2026-06-16 10:36 ` David Laight
2026-06-16 15:47 ` Guo Ren
2026-06-16 15:47 ` Guo Ren
2026-06-16 19:51 ` David Laight
2026-06-16 19:51 ` David Laight
2026-06-17 16:24 ` Guo Ren
2026-06-17 16:24 ` Guo Ren
2026-06-17 21:54 ` David Laight
2026-06-17 21:54 ` David Laight
2026-06-18 3:36 ` Guo Ren
2026-06-18 3:36 ` Guo Ren
2026-06-18 3:20 ` Vivian Wang
2026-06-18 3:20 ` Vivian Wang
2026-06-18 3:45 ` Guo Ren
2026-06-18 3:45 ` Guo Ren
2026-06-18 7:33 ` Vivian Wang
2026-06-18 7:33 ` Vivian Wang
2026-06-18 9:51 ` Guo Ren
2026-06-18 9:51 ` Guo Ren
2026-06-18 10:01 ` Vivian Wang
2026-06-18 10:01 ` Vivian Wang
2026-06-18 13:36 ` David Laight
2026-06-18 13:36 ` David Laight
2026-06-18 16:40 ` Guo Ren
2026-06-18 16:40 ` Guo Ren
2026-06-23 9:20 ` Zong Li
2026-06-23 9:20 ` Zong Li
2026-06-28 8:20 ` Guo Ren
2026-06-28 8:20 ` Guo Ren
2026-06-29 1:15 ` Zong Li
2026-06-29 1:15 ` Zong Li
2026-06-26 9:18 ` Zhanpeng Zhang
2026-06-26 9:18 ` Zhanpeng Zhang
2026-07-13 6:09 ` [PATCH v2] iommu/riscv: Use 32-bit MMIO accesses for 64-bit registers Zhanpeng Zhang
2026-07-13 6:09 ` Zhanpeng Zhang
2026-07-13 7:00 ` Guo Ren
2026-07-13 7:00 ` Guo Ren
2026-07-13 8:24 ` Zhanpeng Zhang
2026-07-13 8:24 ` Zhanpeng Zhang
2026-07-13 12:29 ` [PATCH v3] " Zhanpeng Zhang
2026-07-13 12:29 ` Zhanpeng Zhang
2026-07-14 2:02 ` Guo Ren
2026-07-14 2:02 ` Guo Ren
2026-07-14 7:10 ` Tomasz Jeznach
2026-07-14 7:10 ` Tomasz Jeznach
2026-07-14 12:27 ` Robin Murphy
2026-07-14 12:27 ` Robin Murphy
2026-07-14 13:24 ` Guo Ren
2026-07-14 13:24 ` Guo Ren
2026-07-14 14:53 ` David Laight
2026-07-14 14:53 ` David Laight
2026-07-14 16:53 ` Guo Ren
2026-07-14 16:53 ` Guo Ren
2026-07-14 13:55 ` Guo Ren
2026-07-14 13:55 ` Guo Ren
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.