IRQ bypass maps guest IMSIC addresses through a flat-mode MSI page table owned by the second-stage domain. Allocate the table lazily during device attachment when the attaching IOMMU supports MSI_FLAT, and free it when the domain is destroyed. The required capacity depends on hypervisor support and VMM policy. Expose the runtime-writable riscv_iommu.nr_msi_ptes parameter to set the MSI table capacity of new second-stage domains. It defaults to 512 entries (8 KiB), and zero disables MSI table allocation for new domains. Existing domains retain the capacity selected when they were created. Signed-off-by: Andrew Jones --- drivers/iommu/riscv/iommu.c | 61 +++++++++++++++++++++++++++++++++++++ drivers/iommu/riscv/iommu.h | 7 +++++ 2 files changed, 68 insertions(+) diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c index 0953aaf594cb..3bbb4d0d0a85 100644 --- a/drivers/iommu/riscv/iommu.c +++ b/drivers/iommu/riscv/iommu.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -30,6 +32,16 @@ #include "iommu-bits.h" #include "iommu.h" +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "riscv_iommu." + +#define RISCV_IOMMU_DEFAULT_NR_MSI_PTES 512 + +/* A zero value disables guest MSI table allocation. */ +static unsigned int riscv_iommu_nr_msi_ptes = RISCV_IOMMU_DEFAULT_NR_MSI_PTES; +module_param_named(nr_msi_ptes, riscv_iommu_nr_msi_ptes, uint, 0644); +MODULE_PARM_DESC(nr_msi_ptes, "Number of PTEs for new second-stage domains (default: 512)"); + /* Timeouts in [us] */ #define RISCV_IOMMU_QCSR_TIMEOUT 150000 #define RISCV_IOMMU_QUEUE_TIMEOUT 150000 @@ -847,8 +859,10 @@ struct riscv_iommu_domain { }; struct list_head bonds; spinlock_t lock; /* protect bonds list updates. */ + struct mutex mutex; /* serialize domain state updates */ int pscid; int gscid; + struct riscv_iommu_msi_table msi_table; }; PT_IOMMU_CHECK_DOMAIN(struct riscv_iommu_domain, riscvpt.iommu, domain); @@ -1303,6 +1317,7 @@ static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain) ida_free(&riscv_iommu_gscids, domain->gscid); pt_iommu_deinit(&domain->riscvpt.iommu); + iommu_free_pages(domain->msi_table.root); kfree(domain); } @@ -1338,6 +1353,37 @@ static bool riscv_iommu_iohgatp_supported(struct riscv_iommu_device *iommu, return false; } +static int riscv_iommu_msi_table_alloc(struct riscv_iommu_domain *domain, + struct riscv_iommu_device *iommu) +{ + struct riscv_iommu_msi_table *msi_table = &domain->msi_table; + struct riscv_iommu_msipte *root; + size_t size; + + if (!(iommu->caps & RISCV_IOMMU_CAPABILITIES_MSI_FLAT)) + return 0; + + guard(mutex)(&domain->mutex); + + if (msi_table->root) + return 0; + + if (!msi_table->nr_ptes) + return 0; + + size = array_size(msi_table->nr_ptes, sizeof(*msi_table->root)); + if (size == SIZE_MAX) + return -EOVERFLOW; + + root = iommu_alloc_pages_node_sz(NUMA_NO_NODE, GFP_KERNEL_ACCOUNT, size); + if (!root) + return -ENOMEM; + + msi_table->root = root; + + return 0; +} + static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain, struct device *dev, struct iommu_domain *old) @@ -1347,6 +1393,7 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain, struct riscv_iommu_info *info = dev_iommu_priv_get(dev); struct pt_iommu_riscv_64_hw_info pt_info; struct riscv_iommu_dc dc = {0}; + int ret; pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info); @@ -1354,6 +1401,10 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain, if (!riscv_iommu_iohgatp_supported(iommu, pt_info.iohgatp_mode)) return -ENODEV; + ret = riscv_iommu_msi_table_alloc(domain, iommu); + if (ret) + return ret; + dc.iohgatp = FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_MODE, pt_info.iohgatp_mode) | FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_GSCID, domain->gscid) | @@ -1425,6 +1476,7 @@ riscv_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags, INIT_LIST_HEAD_RCU(&domain->bonds); spin_lock_init(&domain->lock); + mutex_init(&domain->mutex); iommu = dev_to_iommu(dev); cfg.common.hw_max_oasz_lg2 = 56; /* @@ -1484,6 +1536,15 @@ riscv_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags, ret = -ENOMEM; goto err_free; } + + /* + * Capture the current number of MSI PTEs now since it must remain + * stable for the lifetime of the domain. + */ + kernel_param_lock(THIS_MODULE); + domain->msi_table.nr_ptes = riscv_iommu_nr_msi_ptes; + kernel_param_unlock(THIS_MODULE); + cfg.common.features |= BIT(PT_FEAT_RISCV_S2); if (iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD) domain->domain.dirty_ops = &riscv_iommu_dirty_ops; diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h index 6d5c70e9ac6d..6bea9da71ff3 100644 --- a/drivers/iommu/riscv/iommu.h +++ b/drivers/iommu/riscv/iommu.h @@ -68,6 +68,13 @@ struct riscv_iommu_device { u64 *ddt_root; }; +struct riscv_iommu_msi_table { + unsigned int nr_ptes; + struct riscv_iommu_msipte *root; + u64 msi_addr_mask; + u64 msi_addr_pattern; +}; + int riscv_iommu_init(struct riscv_iommu_device *iommu); void riscv_iommu_remove(struct riscv_iommu_device *iommu); void riscv_iommu_disable(struct riscv_iommu_device *iommu); -- 2.43.0