mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 05:48:40 +00:00
iommu/arm-smmu-v3: Link domains and devices
When removing a mapping from a domain, we need to send an invalidation to all devices that might have stored it in their Address Translation Cache (ATC). In addition when updating the context descriptor of a live domain, we'll need to send invalidations for all devices attached to it. Maintain a list of devices in each domain, protected by a spinlock. It is updated every time we attach or detach devices to and from domains. It needs to be a spinlock because we'll invalidate ATC entries from within hardirq-safe contexts, but it may be possible to relax the read side with RCU later. Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
This commit is contained in:
parent
8be39a1a04
commit
2a7e62f516
1 changed files with 20 additions and 1 deletions
|
@ -581,6 +581,7 @@ struct arm_smmu_device {
|
|||
struct arm_smmu_master {
|
||||
struct arm_smmu_device *smmu;
|
||||
struct arm_smmu_domain *domain;
|
||||
struct list_head domain_head;
|
||||
u32 *sids;
|
||||
unsigned int num_sids;
|
||||
};
|
||||
|
@ -607,6 +608,9 @@ struct arm_smmu_domain {
|
|||
};
|
||||
|
||||
struct iommu_domain domain;
|
||||
|
||||
struct list_head devices;
|
||||
spinlock_t devices_lock;
|
||||
};
|
||||
|
||||
struct arm_smmu_option_prop {
|
||||
|
@ -1504,6 +1508,9 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
|
|||
}
|
||||
|
||||
mutex_init(&smmu_domain->init_mutex);
|
||||
INIT_LIST_HEAD(&smmu_domain->devices);
|
||||
spin_lock_init(&smmu_domain->devices_lock);
|
||||
|
||||
return &smmu_domain->domain;
|
||||
}
|
||||
|
||||
|
@ -1721,9 +1728,16 @@ static void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master)
|
|||
|
||||
static void arm_smmu_detach_dev(struct arm_smmu_master *master)
|
||||
{
|
||||
if (!master->domain)
|
||||
unsigned long flags;
|
||||
struct arm_smmu_domain *smmu_domain = master->domain;
|
||||
|
||||
if (!smmu_domain)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&smmu_domain->devices_lock, flags);
|
||||
list_del(&master->domain_head);
|
||||
spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
|
||||
|
||||
master->domain = NULL;
|
||||
arm_smmu_install_ste_for_dev(master);
|
||||
}
|
||||
|
@ -1731,6 +1745,7 @@ static void arm_smmu_detach_dev(struct arm_smmu_master *master)
|
|||
static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
|
||||
struct arm_smmu_device *smmu;
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
|
@ -1764,6 +1779,10 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
|||
|
||||
master->domain = smmu_domain;
|
||||
|
||||
spin_lock_irqsave(&smmu_domain->devices_lock, flags);
|
||||
list_add(&master->domain_head, &smmu_domain->devices);
|
||||
spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
|
||||
|
||||
if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1)
|
||||
arm_smmu_write_ctx_desc(smmu, &smmu_domain->s1_cfg);
|
||||
|
||||
|
|
Loading…
Reference in a new issue