diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c index 3655296c5760..71e481e7fc44 100644 --- a/drivers/dma-buf/heaps/system_heap.c +++ b/drivers/dma-buf/heaps/system_heap.c @@ -11,14 +11,17 @@ */ #include +#include #include #include #include #include +#include #include #include +#include #include -#include +#include #include static struct dma_heap *sys_heap; @@ -59,6 +62,28 @@ static gfp_t order_flags[] = {HIGH_ORDER_GFP, HIGH_ORDER_GFP, LOW_ORDER_GFP}; static const unsigned int orders[] = {8, 4, 0}; #define NUM_ORDERS ARRAY_SIZE(orders) +static bool needs_swiotlb_bounce(struct device *dev, struct sg_table *table) +{ + struct iommu_domain *domain = iommu_get_domain_for_dev(dev); + struct scatterlist *sg; + int i; + + for_each_sgtable_dma_sg(table, sg, i) { + // SG_DMA_SWIOTLB is set only for dma-iommu, not for dma-direct + if (domain && IS_ENABLED(CONFIG_NEED_SG_DMA_FLAGS)) { + if (sg_dma_is_swiotlb(table->sgl)) + return true; + } else { + phys_addr_t paddr = domain ? + iommu_iova_to_phys(domain, sg_dma_address(sg)) : + dma_to_phys(dev, sg_dma_address(sg)); + if (is_swiotlb_buffer(dev, paddr)) + return true; + } + } + return false; +} + static struct sg_table *dup_sg_table(struct sg_table *table) { struct sg_table *new_table; @@ -145,6 +170,13 @@ static struct sg_table *system_heap_map_dma_buf(struct dma_buf_attachment *attac if (ret) return ERR_PTR(ret); + if (a->uncached && needs_swiotlb_bounce(attachment->dev, table)) { + pr_err("Cannot map uncached system heap buffer for %s, as it requires SWIOTLB", + dev_name(attachment->dev)); + dma_unmap_sgtable(attachment->dev, table, direction, attr); + return ERR_PTR(-EINVAL); + } + a->mapped = true; return table; }