|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v4 03/22] xen/arm: smmuv3: Alloc io_domain for each device
From: Rahul Singh <rahul.singh@xxxxxxx>
In current implementation io_domain is allocated once for each xen
domain as Stage2 translation is common for all devices in same xen
domain.
Nested stage supports S1 and S2 configuration at the same time. Stage1
translation will be different for each device as linux kernel will
allocate page-table for each device.
Alloc io_domain for each device so that each device can have different
Stage-1 and Stage-2 configuration structure.
The stage-2 configuration is now shared between devices belonging to
the same domain. Update io_domain initialization to create a new stage-2
configuration only if a VMID has not already been allocated for the
target Xen domain. Similarly, update io_domain teardown to free the
VMID only when no other devices are assigned to the target domain.
Also optimize the IOTLB flush procedure to invalidate each VMID only
once.
Signed-off-by: Rahul Singh <rahul.singh@xxxxxxx>
Signed-off-by: Milan Djokic <milan_djokic@xxxxxxxx>
---
xen/drivers/passthrough/arm/smmu-v3.c | 102 ++++++++++++++++++++++++--
1 file changed, 94 insertions(+), 8 deletions(-)
diff --git a/xen/drivers/passthrough/arm/smmu-v3.c
b/xen/drivers/passthrough/arm/smmu-v3.c
index d3e88d5a55..bd0689664b 100644
--- a/xen/drivers/passthrough/arm/smmu-v3.c
+++ b/xen/drivers/passthrough/arm/smmu-v3.c
@@ -1229,14 +1229,49 @@ static void arm_smmu_bitmap_free(unsigned long *map,
int idx)
clear_bit(idx, map);
}
+static struct arm_smmu_domain *arm_smmu_get_domain_s2(struct domain *d,
+ struct arm_smmu_device *smmu)
+{
+ struct iommu_domain *io_domain;
+ struct arm_smmu_domain *smmu_domain;
+ struct arm_smmu_xen_domain *xen_domain = dom_iommu(d)->arch.priv;
+
+ if (!smmu)
+ return NULL;
+
+ /*
+ * Loop through the &xen_domain->contexts to locate a context
+ * assigned to this SMMU
+ */
+ list_for_each_entry(io_domain, &xen_domain->contexts, list) {
+ smmu_domain = to_smmu_domain(io_domain);
+ if (smmu_domain->smmu == smmu &&
+ smmu_domain->s2_cfg.vmid != INVALID_VMID)
+ return smmu_domain;
+ }
+
+ return NULL;
+}
+
static void arm_smmu_domain_free(struct iommu_domain *domain)
{
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_device *smmu = smmu_domain->smmu;
struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
+ struct arm_smmu_domain *existing;
- if (cfg->vmid)
- arm_smmu_bitmap_free(smmu->vmid_map, cfg->vmid);
+ if (cfg->vmid) {
+ /*
+ * VMIDs are shared across contexts belonging to the same
+ * Xen domain and SMMU. The current domain has already been
+ * removed from the domain list, so this lookup only finds
+ * remaining users of the VMID.
+ */
+ existing = arm_smmu_get_domain_s2(smmu_domain->d, smmu);
+
+ if (!existing)
+ arm_smmu_bitmap_free(smmu->vmid_map, cfg->vmid);
+ }
xfree(smmu_domain);
}
@@ -1316,6 +1351,7 @@ static int arm_smmu_domain_finalise(struct iommu_domain
*domain,
int ret;
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_device *smmu = smmu_domain->smmu;
+ struct arm_smmu_domain *existing;
if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2))
return 0;
@@ -1327,6 +1363,26 @@ static int arm_smmu_domain_finalise(struct iommu_domain
*domain,
*/
smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
+ /*
+ * Look for an already initialized stage-2 configuration for this
+ * Xen domain and SMMU.
+ */
+ existing = arm_smmu_get_domain_s2(smmu_domain->d, smmu);
+
+ if (existing) {
+ /*
+ * Reuse the stage-2 configuration.
+ */
+ smmu_domain->s2_cfg.vmid = existing->s2_cfg.vmid;
+ smmu_domain->s2_cfg.vttbr = existing->s2_cfg.vttbr;
+ smmu_domain->s2_cfg.vtcr = existing->s2_cfg.vtcr;
+ return 0;
+ }
+
+ /*
+ * If no existing stage-2 configuration for this
+ * (Xen domain, SMMU), create it
+ */
ret = arm_smmu_domain_finalise_s2(smmu_domain, master);
if (ret < 0)
return ret;
@@ -2807,20 +2863,40 @@ static uint32_t __ro_after_init platform_features =
ARM_SMMU_FEAT_COHERENCY;
static int __must_check arm_smmu_iotlb_flush_all(struct domain *d)
{
struct arm_smmu_xen_domain *xen_domain = dom_iommu(d)->arch.priv;
- struct iommu_domain *io_domain;
+ struct iommu_domain *io_domain, *prev;
spin_lock(&xen_domain->lock);
list_for_each_entry(io_domain, &xen_domain->contexts, list) {
+ struct arm_smmu_domain *smmu_domain = to_smmu_domain(io_domain);
+
/*
* Only invalidate the context when SMMU is present.
* This is because the context initialization is delayed
* until a master has been added.
*/
- if (unlikely(!ACCESS_ONCE(to_smmu_domain(io_domain)->smmu)))
+ if (unlikely(!ACCESS_ONCE(smmu_domain->smmu)))
continue;
- arm_smmu_tlb_inv_context(to_smmu_domain(io_domain));
+ /*
+ * Skip contexts that share the same SMMU and VMID with an
+ * earlier context.
+ */
+ list_for_each_entry(prev, &xen_domain->contexts, list) {
+ struct arm_smmu_domain *prev_domain =
to_smmu_domain(prev);
+
+ if (prev == io_domain)
+ break;
+
+ if (prev_domain->smmu == smmu_domain->smmu &&
+ prev_domain->s2_cfg.vmid ==
smmu_domain->s2_cfg.vmid)
+ break;
+ }
+
+ if (prev != io_domain)
+ continue;
+
+ arm_smmu_tlb_inv_context(smmu_domain);
}
spin_unlock(&xen_domain->lock);
@@ -2866,12 +2942,22 @@ static struct iommu_domain *arm_smmu_get_domain(struct
domain *d,
/*
* Loop through the &xen_domain->contexts to locate a context
- * assigned to this SMMU
+ * assigned to this device
*/
list_for_each_entry(io_domain, &xen_domain->contexts, list) {
+ unsigned long flags;
+ const struct arm_smmu_master *master;
+
smmu_domain = to_smmu_domain(io_domain);
- if (smmu_domain->smmu == smmu)
- return io_domain;
+
+ spin_lock_irqsave(&smmu_domain->devices_lock, flags);
+ list_for_each_entry(master, &smmu_domain->devices, domain_head)
{
+ if (master->dev == dev) {
+
spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
+ return io_domain;
+ }
+ }
+ spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
}
return NULL;
}
--
2.43.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |