diff options
author | Will Deacon <will.deacon@arm.com> | 2016-02-17 20:41:57 +0300 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2016-02-18 18:02:44 +0300 |
commit | bc7f2ce0a7b54ba7703f81995fe434f0926424d2 (patch) | |
tree | 1e27c323f9c3becd069a36abb54ff2fb2ca7d93b /drivers/iommu/arm-smmu-v3.c | |
parent | 25a1c96cd22c949b50d6f0269c23e1c5cb55e5a5 (diff) | |
download | linux-bc7f2ce0a7b54ba7703f81995fe434f0926424d2.tar.xz |
iommu/arm-smmu: Don't fail device attach if already attached to a domain
The ARM SMMU attach_dev implementations returns -EEXIST if the device
being attached is already attached to a domain. This doesn't play nicely
with the default domain, resulting in splats such as:
WARNING: at drivers/iommu/iommu.c:1257
Modules linked in:
CPU: 3 PID: 1939 Comm: virtio-net-tx Tainted: G S 4.5.0-rc4+ #1
Hardware name: FVP Base (DT)
task: ffffffc87a9d0000 ti: ffffffc07a278000 task.ti: ffffffc07a278000
PC is at __iommu_detach_group+0x68/0xe8
LR is at __iommu_detach_group+0x48/0xe8
This patch fixes the problem by forcefully detaching the device from
its old domain, if present, when attaching to a new one. The unused
->detach_dev callback is also removed the iommu_ops structures.
Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'drivers/iommu/arm-smmu-v3.c')
-rw-r--r-- | drivers/iommu/arm-smmu-v3.c | 33 |
1 files changed, 12 insertions, 21 deletions
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c index f003b3ab646b..d96d45ddf500 100644 --- a/drivers/iommu/arm-smmu-v3.c +++ b/drivers/iommu/arm-smmu-v3.c @@ -1638,6 +1638,17 @@ static int arm_smmu_install_ste_for_group(struct arm_smmu_group *smmu_group) return 0; } +static void arm_smmu_detach_dev(struct device *dev) +{ + struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev); + + smmu_group->ste.bypass = true; + if (IS_ERR_VALUE(arm_smmu_install_ste_for_group(smmu_group))) + dev_warn(dev, "failed to install bypass STE\n"); + + smmu_group->domain = NULL; +} + static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) { int ret = 0; @@ -1650,7 +1661,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) /* Already attached to a different domain? */ if (smmu_group->domain && smmu_group->domain != smmu_domain) - return -EEXIST; + arm_smmu_detach_dev(dev); smmu = smmu_group->smmu; mutex_lock(&smmu_domain->init_mutex); @@ -1687,25 +1698,6 @@ out_unlock: return ret; } -static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) -{ - struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev); - - BUG_ON(!smmu_domain); - BUG_ON(!smmu_group); - - mutex_lock(&smmu_domain->init_mutex); - BUG_ON(smmu_group->domain != smmu_domain); - - smmu_group->ste.bypass = true; - if (IS_ERR_VALUE(arm_smmu_install_ste_for_group(smmu_group))) - dev_warn(dev, "failed to install bypass STE\n"); - - smmu_group->domain = NULL; - mutex_unlock(&smmu_domain->init_mutex); -} - static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t size, int prot) { @@ -1943,7 +1935,6 @@ static struct iommu_ops arm_smmu_ops = { .domain_alloc = arm_smmu_domain_alloc, .domain_free = arm_smmu_domain_free, .attach_dev = arm_smmu_attach_dev, - .detach_dev = arm_smmu_detach_dev, .map = arm_smmu_map, .unmap = arm_smmu_unmap, .iova_to_phys = arm_smmu_iova_to_phys, |