[RFC PATCH v3 12/14] iommu/riscv: Prepare interrupt remapping for IRQ bypass
Andrew Jones
andrew.jones at oss.qualcomm.com
Mon Sep 28 07:31:11 PDT 2026
IRQ bypass changes the meaning of an interrupt from host delivery to
delivery through the guest's MSI table. The transition therefore has to
follow the device's current IOMMU domain and must not race with a domain
replacement.
Wire the interrupt-remapping irqdomain into irq_set_vcpu_affinity() and
serialize the callback with MSI-table publication and attachment. Since
one table is shared by every device in the domain, also require all
forwarded interrupts to agree on its owner and address layout.
Leave the hardware operations as stubs so the locking and state-machine
requirements are established before MSI PTE programming is added.
Signed-off-by: Andrew Jones <andrew.jones at oss.qualcomm.com>
---
drivers/iommu/riscv/iommu-ir.c | 140 ++++++++++++++++++++++++++++++++-
drivers/iommu/riscv/iommu.h | 1 +
2 files changed, 140 insertions(+), 1 deletion(-)
diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c
index c5603cb9f258..22da76987c7c 100644
--- a/drivers/iommu/riscv/iommu-ir.c
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -11,12 +11,150 @@
#include "iommu.h"
+static int riscv_iommu_ir_irq_set_affinity(struct irq_data *data,
+ const struct cpumask *mask, bool force)
+{
+ if (irqd_is_forwarded_to_vcpu(data))
+ return -EINVAL;
+
+ return irq_chip_set_affinity_parent(data, mask, force);
+}
+
+static int riscv_iommu_ir_activate(struct riscv_iommu_msi_table *msi_table,
+ struct riscv_iommu_ir_vcpu_info *vcpu_info)
+{
+ return -EOPNOTSUPP;
+}
+
+static int riscv_iommu_ir_deactivate(struct riscv_iommu_msi_table *msi_table)
+{
+ return -EOPNOTSUPP;
+}
+
+static int riscv_iommu_ir_update_target(struct riscv_iommu_msi_table *msi_table,
+ struct riscv_iommu_ir_vcpu_info *vcpu_info)
+{
+ return -EOPNOTSUPP;
+}
+
+static int riscv_iommu_ir_irq_set_vcpu_affinity_locked(struct irq_data *data,
+ struct riscv_iommu_info *info,
+ struct riscv_iommu_ir_vcpu_info *vcpu_info,
+ struct riscv_iommu_msi_table *msi_table)
+{
+ int ret;
+
+ if (!vcpu_info) {
+ if (WARN_ON_ONCE(!msi_table->nr_forwarded_irqs || !info->nr_forwarded_irqs))
+ return -EINVAL;
+
+ if (msi_table->nr_forwarded_irqs == 1) {
+ ret = riscv_iommu_ir_deactivate(msi_table);
+ if (ret)
+ return ret;
+ }
+
+ msi_table->nr_forwarded_irqs--;
+ info->nr_forwarded_irqs--;
+ irqd_clr_forwarded_to_vcpu(data);
+
+ if (!msi_table->nr_forwarded_irqs) {
+ msi_table->owner = NULL;
+ msi_table->msi_addr_mask = 0;
+ msi_table->msi_addr_pattern = 0;
+ }
+
+ return 0;
+ }
+
+ if (!msi_table->nr_forwarded_irqs) {
+ if (vcpu_info->cmd == RISCV_IOMMU_IR_UPDATE_TARGET ||
+ irqd_is_forwarded_to_vcpu(data))
+ return -EINVAL;
+
+ ret = riscv_iommu_ir_activate(msi_table, vcpu_info);
+ if (ret)
+ return ret;
+
+ msi_table->nr_forwarded_irqs++;
+ info->nr_forwarded_irqs++;
+ irqd_set_forwarded_to_vcpu(data);
+
+ return 0;
+ }
+
+ if (msi_table->owner != vcpu_info->owner ||
+ msi_table->msi_addr_mask != vcpu_info->msi_addr_mask ||
+ msi_table->msi_addr_pattern != vcpu_info->msi_addr_pattern)
+ return -EOPNOTSUPP;
+
+ if (vcpu_info->cmd == RISCV_IOMMU_IR_FORWARD) {
+ if (!irqd_is_forwarded_to_vcpu(data)) {
+ msi_table->nr_forwarded_irqs++;
+ info->nr_forwarded_irqs++;
+ irqd_set_forwarded_to_vcpu(data);
+ }
+ return 0;
+ }
+
+ return riscv_iommu_ir_update_target(msi_table, vcpu_info);
+}
+
+static int riscv_iommu_ir_irq_set_vcpu_affinity(struct irq_data *data, void *arg)
+{
+ struct riscv_iommu_ir_vcpu_info *vcpu_info = arg;
+ struct riscv_iommu_msi_table *msi_table;
+ struct riscv_iommu_info *info;
+ struct msi_desc *desc;
+ struct device *dev;
+ int ret;
+
+ if (!vcpu_info && !irqd_is_forwarded_to_vcpu(data))
+ return 0;
+
+ if (vcpu_info && vcpu_info->cmd != RISCV_IOMMU_IR_FORWARD &&
+ (vcpu_info->cmd != RISCV_IOMMU_IR_UPDATE_TARGET || !irqd_is_forwarded_to_vcpu(data)))
+ return -EINVAL;
+
+ desc = irq_data_get_msi_desc(data);
+ if (WARN_ON_ONCE(!desc))
+ return -EINVAL;
+
+ dev = msi_desc_to_dev(desc);
+ info = dev_iommu_priv_get(dev);
+ if (WARN_ON_ONCE(!info))
+ return -EINVAL;
+
+ scoped_guard(rcu) {
+ /*
+ * RCU keeps the table alive, but the device may switch domains before
+ * the table is locked. Recheck the association under the lock.
+ */
+ for (;;) {
+ msi_table = riscv_iommu_msi_table_rcu(info);
+ if (!msi_table || !msi_table->root)
+ return -EOPNOTSUPP;
+
+ raw_spin_lock(&msi_table->lock);
+ if (msi_table == riscv_iommu_msi_table_rcu(info))
+ break;
+ raw_spin_unlock(&msi_table->lock);
+ }
+ }
+
+ ret = riscv_iommu_ir_irq_set_vcpu_affinity_locked(data, info, vcpu_info, msi_table);
+ raw_spin_unlock(&msi_table->lock);
+
+ return ret;
+}
+
static struct irq_chip riscv_iommu_ir_irq_chip = {
.name = "IOMMU-IR",
.irq_ack = irq_chip_ack_parent,
.irq_mask = irq_chip_mask_parent,
.irq_unmask = irq_chip_unmask_parent,
- .irq_set_affinity = irq_chip_set_affinity_parent,
+ .irq_set_affinity = riscv_iommu_ir_irq_set_affinity,
+ .irq_set_vcpu_affinity = riscv_iommu_ir_irq_set_vcpu_affinity,
};
static int riscv_iommu_ir_irq_domain_alloc_irqs(struct irq_domain *irqdomain,
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index ed973979d795..a42f0b6a88d4 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -82,6 +82,7 @@ struct riscv_iommu_msi_table {
struct riscv_iommu_msipte *root;
u64 msi_addr_mask;
u64 msi_addr_pattern;
+ const void *owner;
};
/* Private IOMMU data for managed devices, dev_iommu_priv_* */
--
2.43.0
More information about the linux-riscv
mailing list