Use it to store all vSMMU-related data. The vsid (Virtual Stream ID) will
be the first use case. Since the vsid reader will be the eventq handler
that already holds a streams_mutex, reuse that to fence the vmaster too.

Also add a pair of arm_smmu_attach_prepare/commit_vmaster helpers to set
or unset the master->vmaster pointer. Put the helpers inside the existing
arm_smmu_attach_prepare/commit().

For identity/blocked ops that don't call arm_smmu_attach_prepare/commit(),
add a simpler arm_smmu_master_clear_vmaster helper to unset the vmaster.

Reviewed-by: Jason Gunthorpe <j...@nvidia.com>
Reviewed-by: Pranjal Shrivastava <pr...@google.com>
Acked-by: Will Deacon <w...@kernel.org>
Signed-off-by: Nicolin Chen <nicol...@nvidia.com>
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h   | 28 +++++++++++++
 .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c     | 41 +++++++++++++++++++
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c   | 18 +++++++-
 3 files changed, 86 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index bd9d7c85576a..557b300a3a0f 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -799,6 +799,11 @@ struct arm_smmu_stream {
        struct rb_node                  node;
 };
 
+struct arm_smmu_vmaster {
+       struct arm_vsmmu                *vsmmu;
+       unsigned long                   vsid;
+};
+
 struct arm_smmu_event {
        u8                              stall : 1,
                                        ssv : 1,
@@ -824,6 +829,7 @@ struct arm_smmu_master {
        struct arm_smmu_device          *smmu;
        struct device                   *dev;
        struct arm_smmu_stream          *streams;
+       struct arm_smmu_vmaster         *vmaster; /* use smmu->streams_mutex */
        /* Locked by the iommu core using the group mutex */
        struct arm_smmu_ctx_desc_cfg    cd_table;
        unsigned int                    num_streams;
@@ -972,6 +978,7 @@ struct arm_smmu_attach_state {
        bool disable_ats;
        ioasid_t ssid;
        /* Resulting state */
+       struct arm_smmu_vmaster *vmaster;
        bool ats_enabled;
 };
 
@@ -1055,9 +1062,30 @@ struct iommufd_viommu *arm_vsmmu_alloc(struct device 
*dev,
                                       struct iommu_domain *parent,
                                       struct iommufd_ctx *ictx,
                                       unsigned int viommu_type);
+int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state,
+                                   struct arm_smmu_nested_domain 
*nested_domain);
+void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state);
+void arm_smmu_master_clear_vmaster(struct arm_smmu_master *master);
 #else
 #define arm_smmu_hw_info NULL
 #define arm_vsmmu_alloc NULL
+
+static inline int
+arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state,
+                               struct arm_smmu_nested_domain *nested_domain)
+{
+       return 0;
+}
+
+static inline void
+arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state)
+{
+}
+
+static inline void
+arm_smmu_master_clear_vmaster(struct arm_smmu_master *master)
+{
+}
 #endif /* CONFIG_ARM_SMMU_V3_IOMMUFD */
 
 #endif /* _ARM_SMMU_V3_H */
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
index 5aa2e7af58b4..dfc80e1a8e2e 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
@@ -85,6 +85,47 @@ static void arm_smmu_make_nested_domain_ste(
        }
 }
 
+int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state,
+                                   struct arm_smmu_nested_domain 
*nested_domain)
+{
+       struct arm_smmu_vmaster *vmaster;
+       unsigned long vsid;
+       int ret;
+
+       iommu_group_mutex_assert(state->master->dev);
+
+       ret = iommufd_viommu_get_vdev_id(&nested_domain->vsmmu->core,
+                                        state->master->dev, &vsid);
+       if (ret)
+               return ret;
+
+       vmaster = kzalloc(sizeof(*vmaster), GFP_KERNEL);
+       if (!vmaster)
+               return -ENOMEM;
+       vmaster->vsmmu = nested_domain->vsmmu;
+       vmaster->vsid = vsid;
+       state->vmaster = vmaster;
+
+       return 0;
+}
+
+void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state)
+{
+       struct arm_smmu_master *master = state->master;
+
+       mutex_lock(&master->smmu->streams_mutex);
+       kfree(master->vmaster);
+       master->vmaster = state->vmaster;
+       mutex_unlock(&master->smmu->streams_mutex);
+}
+
+void arm_smmu_master_clear_vmaster(struct arm_smmu_master *master)
+{
+       struct arm_smmu_attach_state state = { .master = master };
+
+       arm_smmu_attach_commit_vmaster(&state);
+}
+
 static int arm_smmu_attach_dev_nested(struct iommu_domain *domain,
                                      struct device *dev)
 {
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 358072b4e293..964d2cf27d3d 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2803,6 +2803,7 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state 
*state,
        struct arm_smmu_domain *smmu_domain =
                to_smmu_domain_devices(new_domain);
        unsigned long flags;
+       int ret;
 
        /*
         * arm_smmu_share_asid() must not see two domains pointing to the same
@@ -2832,9 +2833,18 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state 
*state,
        }
 
        if (smmu_domain) {
+               if (new_domain->type == IOMMU_DOMAIN_NESTED) {
+                       ret = arm_smmu_attach_prepare_vmaster(
+                               state, to_smmu_nested_domain(new_domain));
+                       if (ret)
+                               return ret;
+               }
+
                master_domain = kzalloc(sizeof(*master_domain), GFP_KERNEL);
-               if (!master_domain)
+               if (!master_domain) {
+                       kfree(state->vmaster);
                        return -ENOMEM;
+               }
                master_domain->master = master;
                master_domain->ssid = state->ssid;
                if (new_domain->type == IOMMU_DOMAIN_NESTED)
@@ -2861,6 +2871,7 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state 
*state,
                        spin_unlock_irqrestore(&smmu_domain->devices_lock,
                                               flags);
                        kfree(master_domain);
+                       kfree(state->vmaster);
                        return -EINVAL;
                }
 
@@ -2893,6 +2904,8 @@ void arm_smmu_attach_commit(struct arm_smmu_attach_state 
*state)
 
        lockdep_assert_held(&arm_smmu_asid_lock);
 
+       arm_smmu_attach_commit_vmaster(state);
+
        if (state->ats_enabled && !master->ats_enabled) {
                arm_smmu_enable_ats(master);
        } else if (state->ats_enabled && master->ats_enabled) {
@@ -3162,6 +3175,7 @@ static int arm_smmu_attach_dev_identity(struct 
iommu_domain *domain,
        struct arm_smmu_ste ste;
        struct arm_smmu_master *master = dev_iommu_priv_get(dev);
 
+       arm_smmu_master_clear_vmaster(master);
        arm_smmu_make_bypass_ste(master->smmu, &ste);
        arm_smmu_attach_dev_ste(domain, dev, &ste, STRTAB_STE_1_S1DSS_BYPASS);
        return 0;
@@ -3180,7 +3194,9 @@ static int arm_smmu_attach_dev_blocked(struct 
iommu_domain *domain,
                                        struct device *dev)
 {
        struct arm_smmu_ste ste;
+       struct arm_smmu_master *master = dev_iommu_priv_get(dev);
 
+       arm_smmu_master_clear_vmaster(master);
        arm_smmu_make_abort_ste(&ste);
        arm_smmu_attach_dev_ste(domain, dev, &ste,
                                STRTAB_STE_1_S1DSS_TERMINATE);
-- 
2.43.0


Reply via email to