summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Gunthorpe <jgg@nvidia.com>2026-05-08 11:53:03 -0300
committerJoerg Roedel <joerg.roedel@amd.com>2026-05-19 10:48:08 +0200
commitdb1aad42ca8b37437846078dfc405abda7868261 (patch)
tree631e6f4444faedb166b6b8ca685f0cc39f33ac09
parent89792629d4b260104555f9719ac84c1afc9a2012 (diff)
downloadlinux-db1aad42ca8b37437846078dfc405abda7868261.tar.gz
linux-db1aad42ca8b37437846078dfc405abda7868261.zip
iommu/riscv: Enable PT_FEAT_DETAILED_GATHER and pass gather to iotlb_inval
RISC-V can use the information from PT_FEAT_DETAILED_GATHER to compute the best stride to generate the single TLB invalidations. Pass the gather down to the lower functions and create a full-range gather for the flush-all callback. Reviewed-by: Tomasz Jeznach <tjeznach@rivosinc.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com> Tested-by: Andrew Jones <andrew.jones@oss.qualcomm.com> Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
-rw-r--r--drivers/iommu/generic_pt/fmt/iommu_riscv64.c4
-rw-r--r--drivers/iommu/riscv/iommu.c44
2 files changed, 31 insertions, 17 deletions
diff --git a/drivers/iommu/generic_pt/fmt/iommu_riscv64.c b/drivers/iommu/generic_pt/fmt/iommu_riscv64.c
index cbf60fffa9bf..b18fc4d109f5 100644
--- a/drivers/iommu/generic_pt/fmt/iommu_riscv64.c
+++ b/drivers/iommu/generic_pt/fmt/iommu_riscv64.c
@@ -6,6 +6,8 @@
#define PT_FMT_VARIANT 64
#define PT_SUPPORTED_FEATURES \
(BIT(PT_FEAT_SIGN_EXTEND) | BIT(PT_FEAT_FLUSH_RANGE) | \
- BIT(PT_FEAT_RISCV_SVNAPOT_64K))
+ BIT(PT_FEAT_RISCV_SVNAPOT_64K) | \
+ BIT(PT_FEAT_DETAILED_GATHER))
+#define PT_FORCE_ENABLED_FEATURES BIT(PT_FEAT_DETAILED_GATHER)
#include "iommu_template.h"
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 6c324f9fdc53..8ea085263f44 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -929,13 +929,29 @@ static void riscv_iommu_bond_unlink(struct riscv_iommu_domain *domain,
#define RISCV_IOMMU_IOTLB_INVAL_LIMIT (2 << 20)
static void riscv_iommu_iotlb_inval(struct riscv_iommu_domain *domain,
- unsigned long start, unsigned long end)
+ struct iommu_iotlb_gather *gather)
{
+ unsigned long start;
+ unsigned long end;
struct riscv_iommu_bond *bond;
struct riscv_iommu_device *iommu, *prev;
struct riscv_iommu_command cmd;
/*
+ * When non-leaf page table entries were changed, the base spec
+ * requires a full PSCID invalidation (AV=0) since there is no
+ * way to do targeted non-leaf invalidation without the NL
+ * extension. Force global invalidation to preserve correctness.
+ */
+ if (gather->pt.table_levels_bitmap) {
+ start = 0;
+ end = ULONG_MAX;
+ } else {
+ start = gather->start;
+ end = gather->end;
+ }
+
+ /*
* For each IOMMU linked with this protection domain (via bonds->dev),
* an IOTLB invaliation command will be submitted and executed.
*
@@ -1145,8 +1161,14 @@ static void riscv_iommu_iodir_update(struct riscv_iommu_device *iommu,
static void riscv_iommu_iotlb_flush_all(struct iommu_domain *iommu_domain)
{
struct riscv_iommu_domain *domain = iommu_domain_to_riscv(iommu_domain);
+ struct iommu_iotlb_gather gather = {
+ .start = 0,
+ .end = ULONG_MAX,
+ .pt.leaf_levels_bitmap = 0xFF,
+ .pt.table_levels_bitmap = 0xFE,
+ };
- riscv_iommu_iotlb_inval(domain, 0, ULONG_MAX);
+ riscv_iommu_iotlb_inval(domain, &gather);
}
static void riscv_iommu_iotlb_sync(struct iommu_domain *iommu_domain,
@@ -1154,19 +1176,8 @@ static void riscv_iommu_iotlb_sync(struct iommu_domain *iommu_domain,
{
struct riscv_iommu_domain *domain = iommu_domain_to_riscv(iommu_domain);
- if (iommu_pages_list_empty(&gather->freelist)) {
- riscv_iommu_iotlb_inval(domain, gather->start, gather->end);
- } else {
- /*
- * In 1.0 spec version, the smallest scope we can use to
- * invalidate all levels of page table (i.e. leaf and non-leaf)
- * is an invalidate-all-PSCID IOTINVAL.VMA with AV=0.
- * This will be updated with hardware support for
- * capability.NL (non-leaf) IOTINVAL command.
- */
- riscv_iommu_iotlb_inval(domain, 0, ULONG_MAX);
- iommu_put_pages_list(&gather->freelist);
- }
+ riscv_iommu_iotlb_inval(domain, gather);
+ iommu_put_pages_list(&gather->freelist);
}
static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
@@ -1267,7 +1278,8 @@ static struct iommu_domain *riscv_iommu_alloc_paging_domain(struct device *dev)
*/
cfg.common.features = BIT(PT_FEAT_SIGN_EXTEND) |
BIT(PT_FEAT_FLUSH_RANGE) |
- BIT(PT_FEAT_RISCV_SVNAPOT_64K);
+ BIT(PT_FEAT_RISCV_SVNAPOT_64K) |
+ BIT(PT_FEAT_DETAILED_GATHER);
if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SVPBMT)
cfg.common.features |= BIT(PT_FEAT_RISCV_SVPBMT);
domain->riscvpt.iommu.nid = dev_to_node(iommu->dev);