summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mm/dma-mapping.c4
-rw-r--r--arch/arm64/mm/dma-mapping.c4
-rw-r--r--drivers/iommu/msm_dma_iommu_mapping.c29
-rw-r--r--include/linux/msm_dma_iommu_mapping.h8
4 files changed, 44 insertions, 1 deletions
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index f5dab4e9fb4b..52486c0e6598 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -28,6 +28,7 @@
#include <linux/vmalloc.h>
#include <linux/sizes.h>
#include <linux/cma.h>
+#include <linux/msm_dma_iommu_mapping.h>
#include <asm/memory.h>
#include <asm/highmem.h>
@@ -2190,6 +2191,9 @@ static void __arm_iommu_detach_device(struct device *dev)
return;
}
+ if (msm_dma_unmap_all_for_dev(dev))
+ dev_warn(dev, "IOMMU detach with outstanding mappings\n");
+
iommu_detach_device(mapping->domain, dev);
kref_put(&mapping->kref, release_iommu_mapping);
to_dma_iommu_mapping(dev) = NULL;
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index 5805312519c7..7109e27de235 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -34,6 +34,7 @@
#include <linux/io.h>
#include <asm/dma-iommu.h>
#include <linux/dma-mapping-fast.h>
+#include <linux/msm_dma_iommu_mapping.h>
#include "mm.h"
@@ -2164,6 +2165,9 @@ void arm_iommu_detach_device(struct device *dev)
iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_S1_BYPASS,
&s1_bypass);
+ if (msm_dma_unmap_all_for_dev(dev))
+ dev_warn(dev, "IOMMU detach with outstanding mappings\n");
+
iommu_detach_device(mapping->domain, dev);
kref_put(&mapping->kref, release_iommu_mapping);
dev->archdata.mapping = NULL;
diff --git a/drivers/iommu/msm_dma_iommu_mapping.c b/drivers/iommu/msm_dma_iommu_mapping.c
index e26b79959b13..a035ebd5af7e 100644
--- a/drivers/iommu/msm_dma_iommu_mapping.c
+++ b/drivers/iommu/msm_dma_iommu_mapping.c
@@ -346,6 +346,35 @@ out:
}
EXPORT_SYMBOL(msm_dma_unmap_sg);
+int msm_dma_unmap_all_for_dev(struct device *dev)
+{
+ int ret = 0;
+ struct msm_iommu_meta *meta;
+ struct rb_root *root;
+ struct rb_node *meta_node;
+
+ mutex_lock(&msm_iommu_map_mutex);
+ root = &iommu_root;
+ meta_node = rb_first(root);
+ while (meta_node) {
+ struct msm_iommu_map *iommu_map;
+
+ meta = rb_entry(meta_node, struct msm_iommu_meta, node);
+ mutex_lock(&meta->lock);
+ list_for_each_entry(iommu_map, &meta->iommu_maps, lnode)
+ if (iommu_map->dev == dev)
+ if (!kref_put(&iommu_map->ref,
+ msm_iommu_map_release))
+ ret = -EINVAL;
+
+ mutex_unlock(&meta->lock);
+ meta_node = rb_next(meta_node);
+ }
+ mutex_unlock(&msm_iommu_map_mutex);
+
+ return ret;
+}
+
/*
* Only to be called by ION code when a buffer is freed
*/
diff --git a/include/linux/msm_dma_iommu_mapping.h b/include/linux/msm_dma_iommu_mapping.h
index 370d6f5e1d65..76451faa2073 100644
--- a/include/linux/msm_dma_iommu_mapping.h
+++ b/include/linux/msm_dma_iommu_mapping.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -50,6 +50,7 @@ static inline int msm_dma_map_sg(struct device *dev, struct scatterlist *sg,
void msm_dma_unmap_sg(struct device *dev, struct scatterlist *sgl, int nents,
enum dma_data_direction dir, struct dma_buf *dma_buf);
+int msm_dma_unmap_all_for_dev(struct device *dev);
/*
* Below is private function only to be called by framework (ION) and not by
@@ -89,6 +90,11 @@ static inline void msm_dma_unmap_sg(struct device *dev,
{
}
+int msm_dma_unmap_all_for_dev(struct device *dev)
+{
+ return 0;
+}
+
static inline void msm_dma_buf_freed(void *buffer) {}
#endif /*CONFIG_IOMMU_API*/