Commit f0ea207e authored by Nicolin Chen's avatar Nicolin Chen Committed by Jason Gunthorpe
Browse files

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: default avatarJason Gunthorpe <jgg@nvidia.com>
Reviewed-by: default avatarPranjal Shrivastava <praan@google.com>
Acked-by: default avatarWill Deacon <will@kernel.org>
Signed-off-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
parent 2ec0458e
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -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)
{
+17 −1
Original line number Diff line number Diff line
@@ -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);
+28 −0
Original line number Diff line number Diff line
@@ -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 */