diff options
author | Will Deacon <will.deacon@arm.com> | 2016-02-17 12:41:57 -0500 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2016-02-18 10:02:44 -0500 |
commit | bc7f2ce0a7b54ba7703f81995fe434f0926424d2 (patch) | |
tree | 1e27c323f9c3becd069a36abb54ff2fb2ca7d93b | |
parent | 25a1c96cd22c949b50d6f0269c23e1c5cb55e5a5 (diff) |
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>
-rw-r--r-- | drivers/iommu/arm-smmu-v3.c | 33 | ||||
-rw-r--r-- | drivers/iommu/arm-smmu.c | 33 |
2 files changed, 26 insertions, 40 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) | |||
1638 | return 0; | 1638 | return 0; |
1639 | } | 1639 | } |
1640 | 1640 | ||
1641 | static void arm_smmu_detach_dev(struct device *dev) | ||
1642 | { | ||
1643 | struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev); | ||
1644 | |||
1645 | smmu_group->ste.bypass = true; | ||
1646 | if (IS_ERR_VALUE(arm_smmu_install_ste_for_group(smmu_group))) | ||
1647 | dev_warn(dev, "failed to install bypass STE\n"); | ||
1648 | |||
1649 | smmu_group->domain = NULL; | ||
1650 | } | ||
1651 | |||
1641 | static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) | 1652 | static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) |
1642 | { | 1653 | { |
1643 | int ret = 0; | 1654 | int ret = 0; |
@@ -1650,7 +1661,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) | |||
1650 | 1661 | ||
1651 | /* Already attached to a different domain? */ | 1662 | /* Already attached to a different domain? */ |
1652 | if (smmu_group->domain && smmu_group->domain != smmu_domain) | 1663 | if (smmu_group->domain && smmu_group->domain != smmu_domain) |
1653 | return -EEXIST; | 1664 | arm_smmu_detach_dev(dev); |
1654 | 1665 | ||
1655 | smmu = smmu_group->smmu; | 1666 | smmu = smmu_group->smmu; |
1656 | mutex_lock(&smmu_domain->init_mutex); | 1667 | mutex_lock(&smmu_domain->init_mutex); |
@@ -1687,25 +1698,6 @@ out_unlock: | |||
1687 | return ret; | 1698 | return ret; |
1688 | } | 1699 | } |
1689 | 1700 | ||
1690 | static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) | ||
1691 | { | ||
1692 | struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); | ||
1693 | struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev); | ||
1694 | |||
1695 | BUG_ON(!smmu_domain); | ||
1696 | BUG_ON(!smmu_group); | ||
1697 | |||
1698 | mutex_lock(&smmu_domain->init_mutex); | ||
1699 | BUG_ON(smmu_group->domain != smmu_domain); | ||
1700 | |||
1701 | smmu_group->ste.bypass = true; | ||
1702 | if (IS_ERR_VALUE(arm_smmu_install_ste_for_group(smmu_group))) | ||
1703 | dev_warn(dev, "failed to install bypass STE\n"); | ||
1704 | |||
1705 | smmu_group->domain = NULL; | ||
1706 | mutex_unlock(&smmu_domain->init_mutex); | ||
1707 | } | ||
1708 | |||
1709 | static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, | 1701 | static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, |
1710 | phys_addr_t paddr, size_t size, int prot) | 1702 | phys_addr_t paddr, size_t size, int prot) |
1711 | { | 1703 | { |
@@ -1943,7 +1935,6 @@ static struct iommu_ops arm_smmu_ops = { | |||
1943 | .domain_alloc = arm_smmu_domain_alloc, | 1935 | .domain_alloc = arm_smmu_domain_alloc, |
1944 | .domain_free = arm_smmu_domain_free, | 1936 | .domain_free = arm_smmu_domain_free, |
1945 | .attach_dev = arm_smmu_attach_dev, | 1937 | .attach_dev = arm_smmu_attach_dev, |
1946 | .detach_dev = arm_smmu_detach_dev, | ||
1947 | .map = arm_smmu_map, | 1938 | .map = arm_smmu_map, |
1948 | .unmap = arm_smmu_unmap, | 1939 | .unmap = arm_smmu_unmap, |
1949 | .iova_to_phys = arm_smmu_iova_to_phys, | 1940 | .iova_to_phys = arm_smmu_iova_to_phys, |
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 7012531abe62..f21332a15040 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c | |||
@@ -1131,6 +1131,16 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain, | |||
1131 | arm_smmu_master_free_smrs(smmu, cfg); | 1131 | arm_smmu_master_free_smrs(smmu, cfg); |
1132 | } | 1132 | } |
1133 | 1133 | ||
1134 | static void arm_smmu_detach_dev(struct device *dev, | ||
1135 | struct arm_smmu_master_cfg *cfg) | ||
1136 | { | ||
1137 | struct iommu_domain *domain = dev->archdata.iommu; | ||
1138 | struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); | ||
1139 | |||
1140 | dev->archdata.iommu = NULL; | ||
1141 | arm_smmu_domain_remove_master(smmu_domain, cfg); | ||
1142 | } | ||
1143 | |||
1134 | static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) | 1144 | static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) |
1135 | { | 1145 | { |
1136 | int ret; | 1146 | int ret; |
@@ -1144,11 +1154,6 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) | |||
1144 | return -ENXIO; | 1154 | return -ENXIO; |
1145 | } | 1155 | } |
1146 | 1156 | ||
1147 | if (dev->archdata.iommu) { | ||
1148 | dev_err(dev, "already attached to IOMMU domain\n"); | ||
1149 | return -EEXIST; | ||
1150 | } | ||
1151 | |||
1152 | /* Ensure that the domain is finalised */ | 1157 | /* Ensure that the domain is finalised */ |
1153 | ret = arm_smmu_init_domain_context(domain, smmu); | 1158 | ret = arm_smmu_init_domain_context(domain, smmu); |
1154 | if (IS_ERR_VALUE(ret)) | 1159 | if (IS_ERR_VALUE(ret)) |
@@ -1170,25 +1175,16 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) | |||
1170 | if (!cfg) | 1175 | if (!cfg) |
1171 | return -ENODEV; | 1176 | return -ENODEV; |
1172 | 1177 | ||
1178 | /* Detach the dev from its current domain */ | ||
1179 | if (dev->archdata.iommu) | ||
1180 | arm_smmu_detach_dev(dev, cfg); | ||
1181 | |||
1173 | ret = arm_smmu_domain_add_master(smmu_domain, cfg); | 1182 | ret = arm_smmu_domain_add_master(smmu_domain, cfg); |
1174 | if (!ret) | 1183 | if (!ret) |
1175 | dev->archdata.iommu = domain; | 1184 | dev->archdata.iommu = domain; |
1176 | return ret; | 1185 | return ret; |
1177 | } | 1186 | } |
1178 | 1187 | ||
1179 | static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) | ||
1180 | { | ||
1181 | struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); | ||
1182 | struct arm_smmu_master_cfg *cfg; | ||
1183 | |||
1184 | cfg = find_smmu_master_cfg(dev); | ||
1185 | if (!cfg) | ||
1186 | return; | ||
1187 | |||
1188 | dev->archdata.iommu = NULL; | ||
1189 | arm_smmu_domain_remove_master(smmu_domain, cfg); | ||
1190 | } | ||
1191 | |||
1192 | static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, | 1188 | static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, |
1193 | phys_addr_t paddr, size_t size, int prot) | 1189 | phys_addr_t paddr, size_t size, int prot) |
1194 | { | 1190 | { |
@@ -1464,7 +1460,6 @@ static struct iommu_ops arm_smmu_ops = { | |||
1464 | .domain_alloc = arm_smmu_domain_alloc, | 1460 | .domain_alloc = arm_smmu_domain_alloc, |
1465 | .domain_free = arm_smmu_domain_free, | 1461 | .domain_free = arm_smmu_domain_free, |
1466 | .attach_dev = arm_smmu_attach_dev, | 1462 | .attach_dev = arm_smmu_attach_dev, |
1467 | .detach_dev = arm_smmu_detach_dev, | ||
1468 | .map = arm_smmu_map, | 1463 | .map = arm_smmu_map, |
1469 | .unmap = arm_smmu_unmap, | 1464 | .unmap = arm_smmu_unmap, |
1470 | .map_sg = default_iommu_map_sg, | 1465 | .map_sg = default_iommu_map_sg, |