Use it to store all vSMMU-related data. The vsid (Virtual Stream ID) will
be the first use case. Then, add a rw_semaphore to protect it.

Also add a pair of arm_smmu_attach_prepare/commit_vmaster helpers to set
or unset the master->vmaster point. Put these helpers inside the existing
arm_smmu_attach_prepare/commit(). Note that identity and blocked ops don't
call arm_smmu_attach_prepare/commit(), thus simply call the new helpers at
the top, so a device attaching to an identity/blocked domain can unset the
master->vmaster when the device is moving away from a nested domain.

Signed-off-by: Nicolin Chen <nicol...@nvidia.com>
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h   | 23 +++++++++
 .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c     | 49 +++++++++++++++++++
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c   | 32 +++++++++++-
 3 files changed, 103 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..4435ad7db776 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,8 @@ struct arm_smmu_master {
        struct arm_smmu_device          *smmu;
        struct device                   *dev;
        struct arm_smmu_stream          *streams;
+       struct arm_smmu_vmaster         *vmaster;
+       struct rw_semaphore             vmaster_rwsem;
        /* Locked by the iommu core using the group mutex */
        struct arm_smmu_ctx_desc_cfg    cd_table;
        unsigned int                    num_streams;
@@ -972,6 +979,7 @@ struct arm_smmu_attach_state {
        bool disable_ats;
        ioasid_t ssid;
        /* Resulting state */
+       struct arm_smmu_vmaster *vmaster;
        bool ats_enabled;
 };
 
@@ -1055,9 +1063,24 @@ 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 iommu_domain *domain);
+void arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state);
 #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 iommu_domain *domain)
+{
+       return 0; /* NOP */
+}
+
+static inline void
+arm_smmu_attach_commit_vmaster(struct arm_smmu_attach_state *state)
+{
+}
 #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 c7cc613050d9..2b6253ef0e8f 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,55 @@ static void arm_smmu_make_nested_domain_ste(
        }
 }
 
+int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state,
+                                   struct iommu_domain *domain)
+{
+       struct arm_smmu_nested_domain *nested_domain;
+       struct arm_smmu_vmaster *vmaster;
+       unsigned long vsid;
+       unsigned int cfg;
+
+       iommu_group_mutex_assert(state->master->dev);
+
+       if (domain->type != IOMMU_DOMAIN_NESTED)
+               return 0;
+       nested_domain = to_smmu_nested_domain(domain);
+
+       /* Skip ABORT/BYPASS or invalid vSTE */
+       cfg = FIELD_GET(STRTAB_STE_0_CFG, le64_to_cpu(nested_domain->ste[0]));
+       if (cfg == STRTAB_STE_0_CFG_ABORT || cfg == STRTAB_STE_0_CFG_BYPASS)
+               return 0;
+       if (!(nested_domain->ste[0] & cpu_to_le64(STRTAB_STE_0_V)))
+               return 0;
+
+       vsid = iommufd_viommu_get_vdev_id(&nested_domain->vsmmu->core,
+                                         state->master->dev);
+       /* Fail the attach if vSID is not correct set by the user space */
+       if (!vsid)
+               return -ENOENT;
+
+       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;
+
+       down_write(&master->vmaster_rwsem);
+       if (state->vmaster != master->vmaster) {
+               kfree(master->vmaster);
+               master->vmaster = state->vmaster;
+       }
+       up_write(&master->vmaster_rwsem);
+}
+
 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 ea76f25c0661..686c171dd273 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2802,6 +2802,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
@@ -2831,9 +2832,15 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state 
*state,
        }
 
        if (smmu_domain) {
+               ret = arm_smmu_attach_prepare_vmaster(state, 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)
@@ -2860,6 +2867,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;
                }
 
@@ -2892,6 +2900,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) {
@@ -3158,8 +3168,17 @@ static void arm_smmu_attach_dev_ste(struct iommu_domain 
*domain,
 static int arm_smmu_attach_dev_identity(struct iommu_domain *domain,
                                        struct device *dev)
 {
+       int ret;
        struct arm_smmu_ste ste;
        struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+       struct arm_smmu_attach_state state = {
+               .master = master,
+       };
+
+       ret = arm_smmu_attach_prepare_vmaster(&state, domain);
+       if (ret)
+               return ret;
+       arm_smmu_attach_commit_vmaster(&state);
 
        arm_smmu_make_bypass_ste(master->smmu, &ste);
        arm_smmu_attach_dev_ste(domain, dev, &ste, STRTAB_STE_1_S1DSS_BYPASS);
@@ -3178,7 +3197,17 @@ static struct iommu_domain arm_smmu_identity_domain = {
 static int arm_smmu_attach_dev_blocked(struct iommu_domain *domain,
                                        struct device *dev)
 {
+       int ret;
        struct arm_smmu_ste ste;
+       struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+       struct arm_smmu_attach_state state = {
+               .master = master,
+       };
+
+       ret = arm_smmu_attach_prepare_vmaster(&state, domain);
+       if (ret)
+               return ret;
+       arm_smmu_attach_commit_vmaster(&state);
 
        arm_smmu_make_abort_ste(&ste);
        arm_smmu_attach_dev_ste(domain, dev, &ste,
@@ -3428,6 +3457,7 @@ static struct iommu_device *arm_smmu_probe_device(struct 
device *dev)
 
        master->dev = dev;
        master->smmu = smmu;
+       init_rwsem(&master->vmaster_rwsem);
        dev_iommu_priv_set(dev, master);
 
        ret = arm_smmu_insert_master(smmu, master);
-- 
2.43.0


Reply via email to