iommu/arm-smmu-v3: Introduce struct arm_smmu_vmaster

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.

Link: https://patch.msgid.link/r/a7f282e1a531279e25f06c651e95d56f6b120886.1741719725.git.nicolinc@nvidia.com
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Pranjal Shrivastava <praan@google.com>
Acked-by: Will Deacon <will@kernel.org>
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
This commit is contained in:
Nicolin Chen 2025-03-11 12:44:30 -07:00 committed by Jason Gunthorpe
parent 2ec0458eb0
commit f0ea207ed7
3 changed files with 86 additions and 1 deletions

View File

@ -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)
{

View File

@ -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);

View File

@ -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 */