iommu/exynos: Support multiple attach_device calls
authorMarek Szyprowski <m.szyprowski@samsung.com>
Thu, 18 Feb 2016 14:13:00 +0000 (15:13 +0100)
committerJoerg Roedel <jroedel@suse.de>
Thu, 25 Feb 2016 14:32:11 +0000 (15:32 +0100)
IOMMU core calls attach_device callback without detaching device from
the previous domain. This patch adds support for such unballanced calls.

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

index 9c8ce951158de92ace43d6c359607d72c4c7d151..b0665042bf29526e26db849e40be425653db0c27 100644 (file)
@@ -201,6 +201,7 @@ static const struct sysmmu_fault_info sysmmu_v5_faults[] = {
 */
 struct exynos_iommu_owner {
        struct list_head controllers;   /* list of sysmmu_drvdata.owner_node */
+       struct iommu_domain *domain;    /* domain this device is attached */
 };
 
 /*
@@ -825,6 +826,41 @@ static void exynos_iommu_domain_free(struct iommu_domain *iommu_domain)
        kfree(domain);
 }
 
+static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain,
+                                   struct device *dev)
+{
+       struct exynos_iommu_owner *owner = dev->archdata.iommu;
+       struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
+       phys_addr_t pagetable = virt_to_phys(domain->pgtable);
+       struct sysmmu_drvdata *data, *next;
+       unsigned long flags;
+       bool found = false;
+
+       if (!has_sysmmu(dev) || owner->domain != iommu_domain)
+               return;
+
+       spin_lock_irqsave(&domain->lock, flags);
+       list_for_each_entry_safe(data, next, &domain->clients, domain_node) {
+               if (data->master == dev) {
+                       if (__sysmmu_disable(data)) {
+                               data->master = NULL;
+                               list_del_init(&data->domain_node);
+                       }
+                       pm_runtime_put(data->sysmmu);
+                       found = true;
+               }
+       }
+       spin_unlock_irqrestore(&domain->lock, flags);
+
+       owner->domain = NULL;
+
+       if (found)
+               dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n",
+                                       __func__, &pagetable);
+       else
+               dev_err(dev, "%s: No IOMMU is attached\n", __func__);
+}
+
 static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain,
                                   struct device *dev)
 {
@@ -838,6 +874,9 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain,
        if (!has_sysmmu(dev))
                return -ENODEV;
 
+       if (owner->domain)
+               exynos_iommu_detach_device(owner->domain, dev);
+
        list_for_each_entry(data, &owner->controllers, owner_node) {
                pm_runtime_get_sync(data->sysmmu);
                ret = __sysmmu_enable(data, pagetable, domain);
@@ -856,44 +895,13 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain,
                return ret;
        }
 
+       owner->domain = iommu_domain;
        dev_dbg(dev, "%s: Attached IOMMU with pgtable %pa %s\n",
                __func__, &pagetable, (ret == 0) ? "" : ", again");
 
        return ret;
 }
 
-static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain,
-                                   struct device *dev)
-{
-       struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
-       phys_addr_t pagetable = virt_to_phys(domain->pgtable);
-       struct sysmmu_drvdata *data, *next;
-       unsigned long flags;
-       bool found = false;
-
-       if (!has_sysmmu(dev))
-               return;
-
-       spin_lock_irqsave(&domain->lock, flags);
-       list_for_each_entry_safe(data, next, &domain->clients, domain_node) {
-               if (data->master == dev) {
-                       if (__sysmmu_disable(data)) {
-                               data->master = NULL;
-                               list_del_init(&data->domain_node);
-                       }
-                       pm_runtime_put(data->sysmmu);
-                       found = true;
-               }
-       }
-       spin_unlock_irqrestore(&domain->lock, flags);
-
-       if (found)
-               dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n",
-                                       __func__, &pagetable);
-       else
-               dev_err(dev, "%s: No IOMMU is attached\n", __func__);
-}
-
 static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *domain,
                sysmmu_pte_t *sent, sysmmu_iova_t iova, short *pgcounter)
 {