iommu/exynos: Rework and fix internal locking
authorMarek Szyprowski <m.szyprowski@samsung.com>
Mon, 14 Nov 2016 10:08:10 +0000 (11:08 +0100)
committerJoerg Roedel <jroedel@suse.de>
Mon, 14 Nov 2016 16:11:59 +0000 (17:11 +0100)
This patch reworks locking in the exynos_iommu_attach/detach_device
functions to ensure that all entries of the sysmmu_drvdata and
exynos_iommu_owner structure are updated under the respective spinlocks,
while runtime pm functions are called without any spinlocks held.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
drivers/iommu/exynos-iommu.c

index 52ded8461a74c15292d8e9d12d640a638dc2df8d..0423dff206d4d3d5bf0e6336407a512cf46ab713 100644 (file)
@@ -769,10 +769,12 @@ static void exynos_iommu_domain_free(struct iommu_domain *iommu_domain)
        spin_lock_irqsave(&domain->lock, flags);
 
        list_for_each_entry_safe(data, next, &domain->clients, domain_node) {
+               spin_lock(&data->lock);
                __sysmmu_disable(data);
                data->pgtable = 0;
                data->domain = NULL;
                list_del_init(&data->domain_node);
+               spin_unlock(&data->lock);
        }
 
        spin_unlock_irqrestore(&domain->lock, flags);
@@ -810,17 +812,22 @@ static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain,
        if (!has_sysmmu(dev) || owner->domain != iommu_domain)
                return;
 
+       list_for_each_entry(data, &owner->controllers, owner_node) {
+               __sysmmu_disable(data);
+               pm_runtime_put(data->sysmmu);
+       }
+
        spin_lock_irqsave(&domain->lock, flags);
        list_for_each_entry_safe(data, next, &domain->clients, domain_node) {
-               __sysmmu_disable(data);
+               spin_lock(&data->lock);
                data->pgtable = 0;
                data->domain = NULL;
                list_del_init(&data->domain_node);
-               pm_runtime_put(data->sysmmu);
+               spin_unlock(&data->lock);
        }
+       owner->domain = NULL;
        spin_unlock_irqrestore(&domain->lock, flags);
 
-       owner->domain = NULL;
 
        dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n", __func__,
                &pagetable);
@@ -841,18 +848,22 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain,
        if (owner->domain)
                exynos_iommu_detach_device(owner->domain, dev);
 
+       spin_lock_irqsave(&domain->lock, flags);
        list_for_each_entry(data, &owner->controllers, owner_node) {
+               spin_lock(&data->lock);
                data->pgtable = pagetable;
                data->domain = domain;
+               list_add_tail(&data->domain_node, &domain->clients);
+               spin_unlock(&data->lock);
+       }
+       owner->domain = iommu_domain;
+       spin_unlock_irqrestore(&domain->lock, flags);
+
+       list_for_each_entry(data, &owner->controllers, owner_node) {
                pm_runtime_get_sync(data->sysmmu);
                __sysmmu_enable(data);
-
-               spin_lock_irqsave(&domain->lock, flags);
-               list_add_tail(&data->domain_node, &domain->clients);
-               spin_unlock_irqrestore(&domain->lock, flags);
        }
 
-       owner->domain = iommu_domain;
        dev_dbg(dev, "%s: Attached IOMMU with pgtable %pa\n", __func__,
                &pagetable);