diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index d41957eae6ef..b663ad2182c3 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -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 06f9ffccd562..eb62c3d36faf 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -34,6 +34,7 @@ #include #include #include +#include #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 b91c7785df0b..0377f08e5007 100644 --- a/drivers/iommu/msm_dma_iommu_mapping.c +++ b/drivers/iommu/msm_dma_iommu_mapping.c @@ -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 @@ -344,6 +344,35 @@ out: return; } +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*/