diff options
author | Will Deacon <will.deacon@arm.com> | 2014-07-15 06:27:08 -0400 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2014-09-16 14:14:16 -0400 |
commit | 8f68f8e28298abdf518648e794c71e534eb8841c (patch) | |
tree | b84a1dd2233efcda165e48446f81e564d636ce1e /drivers/iommu/arm-smmu.c | |
parent | 4cf740b0b6628bda1e5c9201ae0d4f56fc6c06a5 (diff) |
iommu/arm-smmu: add support for multi-master iommu groups
Whilst the driver currently creates one IOMMU group per device, this
will soon change when we start supporting non-transparent PCI bridges
which require all upstream masters to be assigned to the same address
space.
This patch reworks our IOMMU group code so that we can easily support
multi-master groups. The master configuration (streamids and smrs) is
stored as private iommudata on the group, whilst the low-level attach/detach
code is updated to avoid double alloc/free when dealing with multiple
masters sharing the same SMMU configuration. This unifies device
handling, regardless of whether the device sits on the platform or pci
bus.
Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'drivers/iommu/arm-smmu.c')
-rw-r--r-- | drivers/iommu/arm-smmu.c | 65 |
1 files changed, 39 insertions, 26 deletions
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 958ae8194afe..38f8d670afb3 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c | |||
@@ -431,17 +431,17 @@ static void parse_driver_options(struct arm_smmu_device *smmu) | |||
431 | } while (arm_smmu_options[++i].opt); | 431 | } while (arm_smmu_options[++i].opt); |
432 | } | 432 | } |
433 | 433 | ||
434 | static struct device *dev_get_master_dev(struct device *dev) | 434 | static struct device_node *dev_get_dev_node(struct device *dev) |
435 | { | 435 | { |
436 | if (dev_is_pci(dev)) { | 436 | if (dev_is_pci(dev)) { |
437 | struct pci_bus *bus = to_pci_dev(dev)->bus; | 437 | struct pci_bus *bus = to_pci_dev(dev)->bus; |
438 | 438 | ||
439 | while (!pci_is_root_bus(bus)) | 439 | while (!pci_is_root_bus(bus)) |
440 | bus = bus->parent; | 440 | bus = bus->parent; |
441 | return bus->bridge->parent; | 441 | return bus->bridge->parent->of_node; |
442 | } | 442 | } |
443 | 443 | ||
444 | return dev; | 444 | return dev->of_node; |
445 | } | 445 | } |
446 | 446 | ||
447 | static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu, | 447 | static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu, |
@@ -466,15 +466,17 @@ static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu, | |||
466 | } | 466 | } |
467 | 467 | ||
468 | static struct arm_smmu_master_cfg * | 468 | static struct arm_smmu_master_cfg * |
469 | find_smmu_master_cfg(struct arm_smmu_device *smmu, struct device *dev) | 469 | find_smmu_master_cfg(struct device *dev) |
470 | { | 470 | { |
471 | struct arm_smmu_master *master; | 471 | struct arm_smmu_master_cfg *cfg = NULL; |
472 | struct iommu_group *group = iommu_group_get(dev); | ||
472 | 473 | ||
473 | if (dev_is_pci(dev)) | 474 | if (group) { |
474 | return dev->archdata.iommu; | 475 | cfg = iommu_group_get_iommudata(group); |
476 | iommu_group_put(group); | ||
477 | } | ||
475 | 478 | ||
476 | master = find_smmu_master(smmu, dev->of_node); | 479 | return cfg; |
477 | return master ? &master->cfg : NULL; | ||
478 | } | 480 | } |
479 | 481 | ||
480 | static int insert_smmu_master(struct arm_smmu_device *smmu, | 482 | static int insert_smmu_master(struct arm_smmu_device *smmu, |
@@ -550,7 +552,7 @@ static struct arm_smmu_device *find_smmu_for_device(struct device *dev) | |||
550 | { | 552 | { |
551 | struct arm_smmu_device *smmu; | 553 | struct arm_smmu_device *smmu; |
552 | struct arm_smmu_master *master = NULL; | 554 | struct arm_smmu_master *master = NULL; |
553 | struct device_node *dev_node = dev_get_master_dev(dev)->of_node; | 555 | struct device_node *dev_node = dev_get_dev_node(dev); |
554 | 556 | ||
555 | spin_lock(&arm_smmu_devices_lock); | 557 | spin_lock(&arm_smmu_devices_lock); |
556 | list_for_each_entry(smmu, &arm_smmu_devices, list) { | 558 | list_for_each_entry(smmu, &arm_smmu_devices, list) { |
@@ -1156,9 +1158,10 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain, | |||
1156 | struct arm_smmu_device *smmu = smmu_domain->smmu; | 1158 | struct arm_smmu_device *smmu = smmu_domain->smmu; |
1157 | void __iomem *gr0_base = ARM_SMMU_GR0(smmu); | 1159 | void __iomem *gr0_base = ARM_SMMU_GR0(smmu); |
1158 | 1160 | ||
1161 | /* Devices in an IOMMU group may already be configured */ | ||
1159 | ret = arm_smmu_master_configure_smrs(smmu, cfg); | 1162 | ret = arm_smmu_master_configure_smrs(smmu, cfg); |
1160 | if (ret) | 1163 | if (ret) |
1161 | return ret; | 1164 | return ret == -EEXIST ? 0 : ret; |
1162 | 1165 | ||
1163 | for (i = 0; i < cfg->num_streamids; ++i) { | 1166 | for (i = 0; i < cfg->num_streamids; ++i) { |
1164 | u32 idx, s2cr; | 1167 | u32 idx, s2cr; |
@@ -1179,6 +1182,10 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain, | |||
1179 | struct arm_smmu_device *smmu = smmu_domain->smmu; | 1182 | struct arm_smmu_device *smmu = smmu_domain->smmu; |
1180 | void __iomem *gr0_base = ARM_SMMU_GR0(smmu); | 1183 | void __iomem *gr0_base = ARM_SMMU_GR0(smmu); |
1181 | 1184 | ||
1185 | /* An IOMMU group is torn down by the first device to be removed */ | ||
1186 | if ((smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) && !cfg->smrs) | ||
1187 | return; | ||
1188 | |||
1182 | /* | 1189 | /* |
1183 | * We *must* clear the S2CR first, because freeing the SMR means | 1190 | * We *must* clear the S2CR first, because freeing the SMR means |
1184 | * that it can be re-allocated immediately. | 1191 | * that it can be re-allocated immediately. |
@@ -1200,7 +1207,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) | |||
1200 | struct arm_smmu_device *smmu, *dom_smmu; | 1207 | struct arm_smmu_device *smmu, *dom_smmu; |
1201 | struct arm_smmu_master_cfg *cfg; | 1208 | struct arm_smmu_master_cfg *cfg; |
1202 | 1209 | ||
1203 | smmu = dev_get_master_dev(dev)->archdata.iommu; | 1210 | smmu = find_smmu_for_device(dev); |
1204 | if (!smmu) { | 1211 | if (!smmu) { |
1205 | dev_err(dev, "cannot attach to SMMU, is it on the same bus?\n"); | 1212 | dev_err(dev, "cannot attach to SMMU, is it on the same bus?\n"); |
1206 | return -ENXIO; | 1213 | return -ENXIO; |
@@ -1228,7 +1235,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) | |||
1228 | } | 1235 | } |
1229 | 1236 | ||
1230 | /* Looks ok, so add the device to the domain */ | 1237 | /* Looks ok, so add the device to the domain */ |
1231 | cfg = find_smmu_master_cfg(smmu_domain->smmu, dev); | 1238 | cfg = find_smmu_master_cfg(dev); |
1232 | if (!cfg) | 1239 | if (!cfg) |
1233 | return -ENODEV; | 1240 | return -ENODEV; |
1234 | 1241 | ||
@@ -1240,7 +1247,7 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) | |||
1240 | struct arm_smmu_domain *smmu_domain = domain->priv; | 1247 | struct arm_smmu_domain *smmu_domain = domain->priv; |
1241 | struct arm_smmu_master_cfg *cfg; | 1248 | struct arm_smmu_master_cfg *cfg; |
1242 | 1249 | ||
1243 | cfg = find_smmu_master_cfg(smmu_domain->smmu, dev); | 1250 | cfg = find_smmu_master_cfg(dev); |
1244 | if (cfg) | 1251 | if (cfg) |
1245 | arm_smmu_domain_remove_master(smmu_domain, cfg); | 1252 | arm_smmu_domain_remove_master(smmu_domain, cfg); |
1246 | } | 1253 | } |
@@ -1554,17 +1561,19 @@ static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data) | |||
1554 | return 0; /* Continue walking */ | 1561 | return 0; /* Continue walking */ |
1555 | } | 1562 | } |
1556 | 1563 | ||
1564 | static void __arm_smmu_release_pci_iommudata(void *data) | ||
1565 | { | ||
1566 | kfree(data); | ||
1567 | } | ||
1568 | |||
1557 | static int arm_smmu_add_device(struct device *dev) | 1569 | static int arm_smmu_add_device(struct device *dev) |
1558 | { | 1570 | { |
1559 | struct arm_smmu_device *smmu; | 1571 | struct arm_smmu_device *smmu; |
1572 | struct arm_smmu_master_cfg *cfg; | ||
1560 | struct iommu_group *group; | 1573 | struct iommu_group *group; |
1574 | void (*releasefn)(void *) = NULL; | ||
1561 | int ret; | 1575 | int ret; |
1562 | 1576 | ||
1563 | if (dev->archdata.iommu) { | ||
1564 | dev_warn(dev, "IOMMU driver already assigned to device\n"); | ||
1565 | return -EINVAL; | ||
1566 | } | ||
1567 | |||
1568 | smmu = find_smmu_for_device(dev); | 1577 | smmu = find_smmu_for_device(dev); |
1569 | if (!smmu) | 1578 | if (!smmu) |
1570 | return -ENODEV; | 1579 | return -ENODEV; |
@@ -1576,7 +1585,6 @@ static int arm_smmu_add_device(struct device *dev) | |||
1576 | } | 1585 | } |
1577 | 1586 | ||
1578 | if (dev_is_pci(dev)) { | 1587 | if (dev_is_pci(dev)) { |
1579 | struct arm_smmu_master_cfg *cfg; | ||
1580 | struct pci_dev *pdev = to_pci_dev(dev); | 1588 | struct pci_dev *pdev = to_pci_dev(dev); |
1581 | 1589 | ||
1582 | cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); | 1590 | cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); |
@@ -1592,11 +1600,20 @@ static int arm_smmu_add_device(struct device *dev) | |||
1592 | */ | 1600 | */ |
1593 | pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, | 1601 | pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, |
1594 | &cfg->streamids[0]); | 1602 | &cfg->streamids[0]); |
1595 | dev->archdata.iommu = cfg; | 1603 | releasefn = __arm_smmu_release_pci_iommudata; |
1596 | } else { | 1604 | } else { |
1597 | dev->archdata.iommu = smmu; | 1605 | struct arm_smmu_master *master; |
1606 | |||
1607 | master = find_smmu_master(smmu, dev->of_node); | ||
1608 | if (!master) { | ||
1609 | ret = -ENODEV; | ||
1610 | goto out_put_group; | ||
1611 | } | ||
1612 | |||
1613 | cfg = &master->cfg; | ||
1598 | } | 1614 | } |
1599 | 1615 | ||
1616 | iommu_group_set_iommudata(group, cfg, releasefn); | ||
1600 | ret = iommu_group_add_device(group, dev); | 1617 | ret = iommu_group_add_device(group, dev); |
1601 | 1618 | ||
1602 | out_put_group: | 1619 | out_put_group: |
@@ -1606,10 +1623,6 @@ out_put_group: | |||
1606 | 1623 | ||
1607 | static void arm_smmu_remove_device(struct device *dev) | 1624 | static void arm_smmu_remove_device(struct device *dev) |
1608 | { | 1625 | { |
1609 | if (dev_is_pci(dev)) | ||
1610 | kfree(dev->archdata.iommu); | ||
1611 | |||
1612 | dev->archdata.iommu = NULL; | ||
1613 | iommu_group_remove_device(dev); | 1626 | iommu_group_remove_device(dev); |
1614 | } | 1627 | } |
1615 | 1628 | ||