aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2015-01-19 09:27:33 -0500
committerWill Deacon <will.deacon@arm.com>2015-03-27 09:39:36 -0400
commit03edb2264cadddc38ca9030887c2902affbfca3f (patch)
tree98ab9b744ee62676fa714804a072b5e0dd2caf0f
parentf1d84548694fe45f3348c0379d7277fed05bbcf0 (diff)
iommu/arm-smmu: handle multi-alias IOMMU groups for PCI devices
IOMMU groups for PCI devices can correspond to multiple DMA aliases due to things like ACS and PCI quirks. This patch extends the ARM SMMU ->add_device callback so that we consider all of the DMA aliases for a PCI IOMMU group, rather than creating a separate group for each Requester ID. Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r--drivers/iommu/arm-smmu.c92
1 files changed, 57 insertions, 35 deletions
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 161dd46999e2..6ac184669295 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1330,61 +1330,83 @@ static void __arm_smmu_release_pci_iommudata(void *data)
1330 kfree(data); 1330 kfree(data);
1331} 1331}
1332 1332
1333static int arm_smmu_add_device(struct device *dev) 1333static int arm_smmu_add_pci_device(struct pci_dev *pdev)
1334{ 1334{
1335 struct arm_smmu_device *smmu; 1335 int i, ret;
1336 struct arm_smmu_master_cfg *cfg; 1336 u16 sid;
1337 struct iommu_group *group; 1337 struct iommu_group *group;
1338 void (*releasefn)(void *) = NULL; 1338 struct arm_smmu_master_cfg *cfg;
1339 int ret;
1340
1341 smmu = find_smmu_for_device(dev);
1342 if (!smmu)
1343 return -ENODEV;
1344 1339
1345 group = iommu_group_alloc(); 1340 group = iommu_group_get_for_dev(&pdev->dev);
1346 if (IS_ERR(group)) { 1341 if (IS_ERR(group))
1347 dev_err(dev, "Failed to allocate IOMMU group\n");
1348 return PTR_ERR(group); 1342 return PTR_ERR(group);
1349 }
1350
1351 if (dev_is_pci(dev)) {
1352 struct pci_dev *pdev = to_pci_dev(dev);
1353 1343
1344 cfg = iommu_group_get_iommudata(group);
1345 if (!cfg) {
1354 cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 1346 cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
1355 if (!cfg) { 1347 if (!cfg) {
1356 ret = -ENOMEM; 1348 ret = -ENOMEM;
1357 goto out_put_group; 1349 goto out_put_group;
1358 } 1350 }
1359 1351
1360 cfg->num_streamids = 1; 1352 iommu_group_set_iommudata(group, cfg,
1361 /* 1353 __arm_smmu_release_pci_iommudata);
1362 * Assume Stream ID == Requester ID for now. 1354 }
1363 * We need a way to describe the ID mappings in FDT.
1364 */
1365 pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid,
1366 &cfg->streamids[0]);
1367 releasefn = __arm_smmu_release_pci_iommudata;
1368 } else {
1369 struct arm_smmu_master *master;
1370
1371 master = find_smmu_master(smmu, dev->of_node);
1372 if (!master) {
1373 ret = -ENODEV;
1374 goto out_put_group;
1375 }
1376 1355
1377 cfg = &master->cfg; 1356 if (cfg->num_streamids >= MAX_MASTER_STREAMIDS) {
1357 ret = -ENOSPC;
1358 goto out_put_group;
1378 } 1359 }
1379 1360
1380 iommu_group_set_iommudata(group, cfg, releasefn); 1361 /*
1381 ret = iommu_group_add_device(group, dev); 1362 * Assume Stream ID == Requester ID for now.
1363 * We need a way to describe the ID mappings in FDT.
1364 */
1365 pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
1366 for (i = 0; i < cfg->num_streamids; ++i)
1367 if (cfg->streamids[i] == sid)
1368 break;
1369
1370 /* Avoid duplicate SIDs, as this can lead to SMR conflicts */
1371 if (i == cfg->num_streamids)
1372 cfg->streamids[cfg->num_streamids++] = sid;
1382 1373
1374 return 0;
1383out_put_group: 1375out_put_group:
1384 iommu_group_put(group); 1376 iommu_group_put(group);
1385 return ret; 1377 return ret;
1386} 1378}
1387 1379
1380static int arm_smmu_add_platform_device(struct device *dev)
1381{
1382 struct iommu_group *group;
1383 struct arm_smmu_master *master;
1384 struct arm_smmu_device *smmu = find_smmu_for_device(dev);
1385
1386 if (!smmu)
1387 return -ENODEV;
1388
1389 master = find_smmu_master(smmu, dev->of_node);
1390 if (!master)
1391 return -ENODEV;
1392
1393 /* No automatic group creation for platform devices */
1394 group = iommu_group_alloc();
1395 if (IS_ERR(group))
1396 return PTR_ERR(group);
1397
1398 iommu_group_set_iommudata(group, &master->cfg, NULL);
1399 return iommu_group_add_device(group, dev);
1400}
1401
1402static int arm_smmu_add_device(struct device *dev)
1403{
1404 if (dev_is_pci(dev))
1405 return arm_smmu_add_pci_device(to_pci_dev(dev));
1406
1407 return arm_smmu_add_platform_device(dev);
1408}
1409
1388static void arm_smmu_remove_device(struct device *dev) 1410static void arm_smmu_remove_device(struct device *dev)
1389{ 1411{
1390 iommu_group_remove_device(dev); 1412 iommu_group_remove_device(dev);