dma-mapping: direct calls for dma-iommu
Directly call into dma-iommu just like we have been doing for dma-direct for a while. This avoids the indirect call overhead for IOMMU ops and removes the need to have DMA ops entirely for many common configurations. Signed-off-by: Leon Romanovsky <leonro@nvidia.com> Signed-off-by: Leon Romanovsky <leon@kernel.org> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Acked-by: Robin Murphy <robin.murphy@arm.com> Signed-off-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
f69e342eec
commit
b5c58b2fdc
@ -11722,6 +11722,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux.git
|
|||||||
F: drivers/iommu/dma-iommu.c
|
F: drivers/iommu/dma-iommu.c
|
||||||
F: drivers/iommu/dma-iommu.h
|
F: drivers/iommu/dma-iommu.h
|
||||||
F: drivers/iommu/iova.c
|
F: drivers/iommu/iova.c
|
||||||
|
F: include/linux/iommu-dma.h
|
||||||
F: include/linux/iova.h
|
F: include/linux/iova.h
|
||||||
|
|
||||||
IOMMU SUBSYSTEM
|
IOMMU SUBSYSTEM
|
||||||
|
@ -151,7 +151,7 @@ config OF_IOMMU
|
|||||||
# IOMMU-agnostic DMA-mapping layer
|
# IOMMU-agnostic DMA-mapping layer
|
||||||
config IOMMU_DMA
|
config IOMMU_DMA
|
||||||
def_bool ARM64 || X86 || S390
|
def_bool ARM64 || X86 || S390
|
||||||
select DMA_OPS
|
select DMA_OPS_HELPERS
|
||||||
select IOMMU_API
|
select IOMMU_API
|
||||||
select IOMMU_IOVA
|
select IOMMU_IOVA
|
||||||
select IRQ_MSI_IOMMU
|
select IRQ_MSI_IOMMU
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
#include <linux/huge_mm.h>
|
#include <linux/huge_mm.h>
|
||||||
#include <linux/iommu.h>
|
#include <linux/iommu.h>
|
||||||
|
#include <linux/iommu-dma.h>
|
||||||
#include <linux/iova.h>
|
#include <linux/iova.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/list_sort.h>
|
#include <linux/list_sort.h>
|
||||||
@ -1037,9 +1038,8 @@ out_unmap:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sg_table *iommu_dma_alloc_noncontiguous(struct device *dev,
|
struct sg_table *iommu_dma_alloc_noncontiguous(struct device *dev, size_t size,
|
||||||
size_t size, enum dma_data_direction dir, gfp_t gfp,
|
enum dma_data_direction dir, gfp_t gfp, unsigned long attrs)
|
||||||
unsigned long attrs)
|
|
||||||
{
|
{
|
||||||
struct dma_sgt_handle *sh;
|
struct dma_sgt_handle *sh;
|
||||||
|
|
||||||
@ -1055,7 +1055,7 @@ static struct sg_table *iommu_dma_alloc_noncontiguous(struct device *dev,
|
|||||||
return &sh->sgt;
|
return &sh->sgt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iommu_dma_free_noncontiguous(struct device *dev, size_t size,
|
void iommu_dma_free_noncontiguous(struct device *dev, size_t size,
|
||||||
struct sg_table *sgt, enum dma_data_direction dir)
|
struct sg_table *sgt, enum dma_data_direction dir)
|
||||||
{
|
{
|
||||||
struct dma_sgt_handle *sh = sgt_handle(sgt);
|
struct dma_sgt_handle *sh = sgt_handle(sgt);
|
||||||
@ -1066,8 +1066,8 @@ static void iommu_dma_free_noncontiguous(struct device *dev, size_t size,
|
|||||||
kfree(sh);
|
kfree(sh);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iommu_dma_sync_single_for_cpu(struct device *dev,
|
void iommu_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
|
||||||
dma_addr_t dma_handle, size_t size, enum dma_data_direction dir)
|
size_t size, enum dma_data_direction dir)
|
||||||
{
|
{
|
||||||
phys_addr_t phys;
|
phys_addr_t phys;
|
||||||
|
|
||||||
@ -1081,8 +1081,8 @@ static void iommu_dma_sync_single_for_cpu(struct device *dev,
|
|||||||
swiotlb_sync_single_for_cpu(dev, phys, size, dir);
|
swiotlb_sync_single_for_cpu(dev, phys, size, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iommu_dma_sync_single_for_device(struct device *dev,
|
void iommu_dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle,
|
||||||
dma_addr_t dma_handle, size_t size, enum dma_data_direction dir)
|
size_t size, enum dma_data_direction dir)
|
||||||
{
|
{
|
||||||
phys_addr_t phys;
|
phys_addr_t phys;
|
||||||
|
|
||||||
@ -1096,9 +1096,8 @@ static void iommu_dma_sync_single_for_device(struct device *dev,
|
|||||||
arch_sync_dma_for_device(phys, size, dir);
|
arch_sync_dma_for_device(phys, size, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iommu_dma_sync_sg_for_cpu(struct device *dev,
|
void iommu_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl,
|
||||||
struct scatterlist *sgl, int nelems,
|
int nelems, enum dma_data_direction dir)
|
||||||
enum dma_data_direction dir)
|
|
||||||
{
|
{
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
int i;
|
int i;
|
||||||
@ -1112,9 +1111,8 @@ static void iommu_dma_sync_sg_for_cpu(struct device *dev,
|
|||||||
arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir);
|
arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iommu_dma_sync_sg_for_device(struct device *dev,
|
void iommu_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sgl,
|
||||||
struct scatterlist *sgl, int nelems,
|
int nelems, enum dma_data_direction dir)
|
||||||
enum dma_data_direction dir)
|
|
||||||
{
|
{
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
int i;
|
int i;
|
||||||
@ -1129,9 +1127,9 @@ static void iommu_dma_sync_sg_for_device(struct device *dev,
|
|||||||
arch_sync_dma_for_device(sg_phys(sg), sg->length, dir);
|
arch_sync_dma_for_device(sg_phys(sg), sg->length, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
static dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
|
dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
|
||||||
unsigned long offset, size_t size, enum dma_data_direction dir,
|
unsigned long offset, size_t size, enum dma_data_direction dir,
|
||||||
unsigned long attrs)
|
unsigned long attrs)
|
||||||
{
|
{
|
||||||
phys_addr_t phys = page_to_phys(page) + offset;
|
phys_addr_t phys = page_to_phys(page) + offset;
|
||||||
bool coherent = dev_is_dma_coherent(dev);
|
bool coherent = dev_is_dma_coherent(dev);
|
||||||
@ -1189,7 +1187,7 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
|
|||||||
return iova;
|
return iova;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iommu_dma_unmap_page(struct device *dev, dma_addr_t dma_handle,
|
void iommu_dma_unmap_page(struct device *dev, dma_addr_t dma_handle,
|
||||||
size_t size, enum dma_data_direction dir, unsigned long attrs)
|
size_t size, enum dma_data_direction dir, unsigned long attrs)
|
||||||
{
|
{
|
||||||
struct iommu_domain *domain = iommu_get_dma_domain(dev);
|
struct iommu_domain *domain = iommu_get_dma_domain(dev);
|
||||||
@ -1342,8 +1340,8 @@ out_unmap:
|
|||||||
* impedance-matching, to be able to hand off a suitably-aligned list,
|
* impedance-matching, to be able to hand off a suitably-aligned list,
|
||||||
* but still preserve the original offsets and sizes for the caller.
|
* but still preserve the original offsets and sizes for the caller.
|
||||||
*/
|
*/
|
||||||
static int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
|
int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
|
||||||
int nents, enum dma_data_direction dir, unsigned long attrs)
|
enum dma_data_direction dir, unsigned long attrs)
|
||||||
{
|
{
|
||||||
struct iommu_domain *domain = iommu_get_dma_domain(dev);
|
struct iommu_domain *domain = iommu_get_dma_domain(dev);
|
||||||
struct iommu_dma_cookie *cookie = domain->iova_cookie;
|
struct iommu_dma_cookie *cookie = domain->iova_cookie;
|
||||||
@ -1462,8 +1460,8 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg,
|
void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
|
||||||
int nents, enum dma_data_direction dir, unsigned long attrs)
|
enum dma_data_direction dir, unsigned long attrs)
|
||||||
{
|
{
|
||||||
dma_addr_t end = 0, start;
|
dma_addr_t end = 0, start;
|
||||||
struct scatterlist *tmp;
|
struct scatterlist *tmp;
|
||||||
@ -1512,7 +1510,7 @@ static void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg,
|
|||||||
__iommu_dma_unmap(dev, start, end - start);
|
__iommu_dma_unmap(dev, start, end - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
static dma_addr_t iommu_dma_map_resource(struct device *dev, phys_addr_t phys,
|
dma_addr_t iommu_dma_map_resource(struct device *dev, phys_addr_t phys,
|
||||||
size_t size, enum dma_data_direction dir, unsigned long attrs)
|
size_t size, enum dma_data_direction dir, unsigned long attrs)
|
||||||
{
|
{
|
||||||
return __iommu_dma_map(dev, phys, size,
|
return __iommu_dma_map(dev, phys, size,
|
||||||
@ -1520,7 +1518,7 @@ static dma_addr_t iommu_dma_map_resource(struct device *dev, phys_addr_t phys,
|
|||||||
dma_get_mask(dev));
|
dma_get_mask(dev));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
|
void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
|
||||||
size_t size, enum dma_data_direction dir, unsigned long attrs)
|
size_t size, enum dma_data_direction dir, unsigned long attrs)
|
||||||
{
|
{
|
||||||
__iommu_dma_unmap(dev, handle, size);
|
__iommu_dma_unmap(dev, handle, size);
|
||||||
@ -1557,7 +1555,7 @@ static void __iommu_dma_free(struct device *dev, size_t size, void *cpu_addr)
|
|||||||
dma_free_contiguous(dev, page, alloc_size);
|
dma_free_contiguous(dev, page, alloc_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iommu_dma_free(struct device *dev, size_t size, void *cpu_addr,
|
void iommu_dma_free(struct device *dev, size_t size, void *cpu_addr,
|
||||||
dma_addr_t handle, unsigned long attrs)
|
dma_addr_t handle, unsigned long attrs)
|
||||||
{
|
{
|
||||||
__iommu_dma_unmap(dev, handle, size);
|
__iommu_dma_unmap(dev, handle, size);
|
||||||
@ -1601,8 +1599,8 @@ out_free_pages:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *iommu_dma_alloc(struct device *dev, size_t size,
|
void *iommu_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
|
||||||
dma_addr_t *handle, gfp_t gfp, unsigned long attrs)
|
gfp_t gfp, unsigned long attrs)
|
||||||
{
|
{
|
||||||
bool coherent = dev_is_dma_coherent(dev);
|
bool coherent = dev_is_dma_coherent(dev);
|
||||||
int ioprot = dma_info_to_prot(DMA_BIDIRECTIONAL, coherent, attrs);
|
int ioprot = dma_info_to_prot(DMA_BIDIRECTIONAL, coherent, attrs);
|
||||||
@ -1635,7 +1633,7 @@ static void *iommu_dma_alloc(struct device *dev, size_t size,
|
|||||||
return cpu_addr;
|
return cpu_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int iommu_dma_mmap(struct device *dev, struct vm_area_struct *vma,
|
int iommu_dma_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||||
unsigned long attrs)
|
unsigned long attrs)
|
||||||
{
|
{
|
||||||
@ -1666,7 +1664,7 @@ static int iommu_dma_mmap(struct device *dev, struct vm_area_struct *vma,
|
|||||||
vma->vm_page_prot);
|
vma->vm_page_prot);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int iommu_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
|
int iommu_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
|
||||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||||
unsigned long attrs)
|
unsigned long attrs)
|
||||||
{
|
{
|
||||||
@ -1693,19 +1691,19 @@ static int iommu_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long iommu_dma_get_merge_boundary(struct device *dev)
|
unsigned long iommu_dma_get_merge_boundary(struct device *dev)
|
||||||
{
|
{
|
||||||
struct iommu_domain *domain = iommu_get_dma_domain(dev);
|
struct iommu_domain *domain = iommu_get_dma_domain(dev);
|
||||||
|
|
||||||
return (1UL << __ffs(domain->pgsize_bitmap)) - 1;
|
return (1UL << __ffs(domain->pgsize_bitmap)) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t iommu_dma_opt_mapping_size(void)
|
size_t iommu_dma_opt_mapping_size(void)
|
||||||
{
|
{
|
||||||
return iova_rcache_range();
|
return iova_rcache_range();
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t iommu_dma_max_mapping_size(struct device *dev)
|
size_t iommu_dma_max_mapping_size(struct device *dev)
|
||||||
{
|
{
|
||||||
if (dev_is_untrusted(dev))
|
if (dev_is_untrusted(dev))
|
||||||
return swiotlb_max_mapping_size(dev);
|
return swiotlb_max_mapping_size(dev);
|
||||||
@ -1713,32 +1711,6 @@ static size_t iommu_dma_max_mapping_size(struct device *dev)
|
|||||||
return SIZE_MAX;
|
return SIZE_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct dma_map_ops iommu_dma_ops = {
|
|
||||||
.flags = DMA_F_PCI_P2PDMA_SUPPORTED |
|
|
||||||
DMA_F_CAN_SKIP_SYNC,
|
|
||||||
.alloc = iommu_dma_alloc,
|
|
||||||
.free = iommu_dma_free,
|
|
||||||
.alloc_pages_op = dma_common_alloc_pages,
|
|
||||||
.free_pages = dma_common_free_pages,
|
|
||||||
.alloc_noncontiguous = iommu_dma_alloc_noncontiguous,
|
|
||||||
.free_noncontiguous = iommu_dma_free_noncontiguous,
|
|
||||||
.mmap = iommu_dma_mmap,
|
|
||||||
.get_sgtable = iommu_dma_get_sgtable,
|
|
||||||
.map_page = iommu_dma_map_page,
|
|
||||||
.unmap_page = iommu_dma_unmap_page,
|
|
||||||
.map_sg = iommu_dma_map_sg,
|
|
||||||
.unmap_sg = iommu_dma_unmap_sg,
|
|
||||||
.sync_single_for_cpu = iommu_dma_sync_single_for_cpu,
|
|
||||||
.sync_single_for_device = iommu_dma_sync_single_for_device,
|
|
||||||
.sync_sg_for_cpu = iommu_dma_sync_sg_for_cpu,
|
|
||||||
.sync_sg_for_device = iommu_dma_sync_sg_for_device,
|
|
||||||
.map_resource = iommu_dma_map_resource,
|
|
||||||
.unmap_resource = iommu_dma_unmap_resource,
|
|
||||||
.get_merge_boundary = iommu_dma_get_merge_boundary,
|
|
||||||
.opt_mapping_size = iommu_dma_opt_mapping_size,
|
|
||||||
.max_mapping_size = iommu_dma_max_mapping_size,
|
|
||||||
};
|
|
||||||
|
|
||||||
void iommu_setup_dma_ops(struct device *dev)
|
void iommu_setup_dma_ops(struct device *dev)
|
||||||
{
|
{
|
||||||
struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
|
struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
|
||||||
@ -1746,19 +1718,15 @@ void iommu_setup_dma_ops(struct device *dev)
|
|||||||
if (dev_is_pci(dev))
|
if (dev_is_pci(dev))
|
||||||
dev->iommu->pci_32bit_workaround = !iommu_dma_forcedac;
|
dev->iommu->pci_32bit_workaround = !iommu_dma_forcedac;
|
||||||
|
|
||||||
if (iommu_is_dma_domain(domain)) {
|
dev->dma_iommu = iommu_is_dma_domain(domain);
|
||||||
if (iommu_dma_init_domain(domain, dev))
|
if (dev->dma_iommu && iommu_dma_init_domain(domain, dev))
|
||||||
goto out_err;
|
goto out_err;
|
||||||
dev->dma_ops = &iommu_dma_ops;
|
|
||||||
} else if (dev->dma_ops == &iommu_dma_ops) {
|
|
||||||
/* Clean up if we've switched *from* a DMA domain */
|
|
||||||
dev->dma_ops = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
out_err:
|
out_err:
|
||||||
pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
|
pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
|
||||||
dev_name(dev));
|
dev_name(dev));
|
||||||
|
dev->dma_iommu = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
|
static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
|
||||||
|
@ -12,7 +12,6 @@ config DMAR_DEBUG
|
|||||||
config INTEL_IOMMU
|
config INTEL_IOMMU
|
||||||
bool "Support for Intel IOMMU using DMA Remapping Devices"
|
bool "Support for Intel IOMMU using DMA Remapping Devices"
|
||||||
depends on PCI_MSI && ACPI && X86
|
depends on PCI_MSI && ACPI && X86
|
||||||
select DMA_OPS
|
|
||||||
select IOMMU_API
|
select IOMMU_API
|
||||||
select IOMMU_IOVA
|
select IOMMU_IOVA
|
||||||
select IOMMUFD_DRIVER if IOMMUFD
|
select IOMMUFD_DRIVER if IOMMUFD
|
||||||
|
@ -707,6 +707,8 @@ struct device_physical_location {
|
|||||||
* for dma allocations. This flag is managed by the dma ops
|
* for dma allocations. This flag is managed by the dma ops
|
||||||
* instance from ->dma_supported.
|
* instance from ->dma_supported.
|
||||||
* @dma_skip_sync: DMA sync operations can be skipped for coherent buffers.
|
* @dma_skip_sync: DMA sync operations can be skipped for coherent buffers.
|
||||||
|
* @dma_iommu: Device is using default IOMMU implementation for DMA and
|
||||||
|
* doesn't rely on dma_ops structure.
|
||||||
*
|
*
|
||||||
* At the lowest level, every device in a Linux system is represented by an
|
* At the lowest level, every device in a Linux system is represented by an
|
||||||
* instance of struct device. The device structure contains the information
|
* instance of struct device. The device structure contains the information
|
||||||
@ -822,6 +824,9 @@ struct device {
|
|||||||
#ifdef CONFIG_DMA_NEED_SYNC
|
#ifdef CONFIG_DMA_NEED_SYNC
|
||||||
bool dma_skip_sync:1;
|
bool dma_skip_sync:1;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_IOMMU_DMA
|
||||||
|
bool dma_iommu:1;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,20 +13,7 @@
|
|||||||
struct cma;
|
struct cma;
|
||||||
struct iommu_ops;
|
struct iommu_ops;
|
||||||
|
|
||||||
/*
|
|
||||||
* Values for struct dma_map_ops.flags:
|
|
||||||
*
|
|
||||||
* DMA_F_PCI_P2PDMA_SUPPORTED: Indicates the dma_map_ops implementation can
|
|
||||||
* handle PCI P2PDMA pages in the map_sg/unmap_sg operation.
|
|
||||||
* DMA_F_CAN_SKIP_SYNC: DMA sync operations can be skipped if the device is
|
|
||||||
* coherent and it's not an SWIOTLB buffer.
|
|
||||||
*/
|
|
||||||
#define DMA_F_PCI_P2PDMA_SUPPORTED (1 << 0)
|
|
||||||
#define DMA_F_CAN_SKIP_SYNC (1 << 1)
|
|
||||||
|
|
||||||
struct dma_map_ops {
|
struct dma_map_ops {
|
||||||
unsigned int flags;
|
|
||||||
|
|
||||||
void *(*alloc)(struct device *dev, size_t size,
|
void *(*alloc)(struct device *dev, size_t size,
|
||||||
dma_addr_t *dma_handle, gfp_t gfp,
|
dma_addr_t *dma_handle, gfp_t gfp,
|
||||||
unsigned long attrs);
|
unsigned long attrs);
|
||||||
|
147
include/linux/iommu-dma.h
Normal file
147
include/linux/iommu-dma.h
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved
|
||||||
|
*
|
||||||
|
* DMA operations that map physical memory through IOMMU.
|
||||||
|
*/
|
||||||
|
#ifndef _LINUX_IOMMU_DMA_H
|
||||||
|
#define _LINUX_IOMMU_DMA_H
|
||||||
|
|
||||||
|
#include <linux/dma-direction.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_IOMMU_DMA
|
||||||
|
dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
|
||||||
|
unsigned long offset, size_t size, enum dma_data_direction dir,
|
||||||
|
unsigned long attrs);
|
||||||
|
void iommu_dma_unmap_page(struct device *dev, dma_addr_t dma_handle,
|
||||||
|
size_t size, enum dma_data_direction dir, unsigned long attrs);
|
||||||
|
int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
|
||||||
|
enum dma_data_direction dir, unsigned long attrs);
|
||||||
|
void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
|
||||||
|
enum dma_data_direction dir, unsigned long attrs);
|
||||||
|
void *iommu_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
|
||||||
|
gfp_t gfp, unsigned long attrs);
|
||||||
|
int iommu_dma_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||||
|
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||||
|
unsigned long attrs);
|
||||||
|
int iommu_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
|
||||||
|
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||||
|
unsigned long attrs);
|
||||||
|
unsigned long iommu_dma_get_merge_boundary(struct device *dev);
|
||||||
|
size_t iommu_dma_opt_mapping_size(void);
|
||||||
|
size_t iommu_dma_max_mapping_size(struct device *dev);
|
||||||
|
void iommu_dma_free(struct device *dev, size_t size, void *cpu_addr,
|
||||||
|
dma_addr_t handle, unsigned long attrs);
|
||||||
|
dma_addr_t iommu_dma_map_resource(struct device *dev, phys_addr_t phys,
|
||||||
|
size_t size, enum dma_data_direction dir, unsigned long attrs);
|
||||||
|
void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
|
||||||
|
size_t size, enum dma_data_direction dir, unsigned long attrs);
|
||||||
|
struct sg_table *iommu_dma_alloc_noncontiguous(struct device *dev, size_t size,
|
||||||
|
enum dma_data_direction dir, gfp_t gfp, unsigned long attrs);
|
||||||
|
void iommu_dma_free_noncontiguous(struct device *dev, size_t size,
|
||||||
|
struct sg_table *sgt, enum dma_data_direction dir);
|
||||||
|
void iommu_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
|
||||||
|
size_t size, enum dma_data_direction dir);
|
||||||
|
void iommu_dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle,
|
||||||
|
size_t size, enum dma_data_direction dir);
|
||||||
|
void iommu_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl,
|
||||||
|
int nelems, enum dma_data_direction dir);
|
||||||
|
void iommu_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sgl,
|
||||||
|
int nelems, enum dma_data_direction dir);
|
||||||
|
#else
|
||||||
|
static inline dma_addr_t iommu_dma_map_page(struct device *dev,
|
||||||
|
struct page *page, unsigned long offset, size_t size,
|
||||||
|
enum dma_data_direction dir, unsigned long attrs)
|
||||||
|
{
|
||||||
|
return DMA_MAPPING_ERROR;
|
||||||
|
}
|
||||||
|
static inline void iommu_dma_unmap_page(struct device *dev,
|
||||||
|
dma_addr_t dma_handle, size_t size, enum dma_data_direction dir,
|
||||||
|
unsigned long attrs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
static inline int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
|
||||||
|
int nents, enum dma_data_direction dir, unsigned long attrs)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
static inline void iommu_dma_unmap_sg(struct device *dev,
|
||||||
|
struct scatterlist *sg, int nents, enum dma_data_direction dir,
|
||||||
|
unsigned long attrs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
static inline void *iommu_dma_alloc(struct device *dev, size_t size,
|
||||||
|
dma_addr_t *handle, gfp_t gfp, unsigned long attrs)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
static inline int iommu_dma_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||||
|
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||||
|
unsigned long attrs)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
static inline int iommu_dma_get_sgtable(struct device *dev,
|
||||||
|
struct sg_table *sgt, void *cpu_addr, dma_addr_t dma_addr,
|
||||||
|
size_t size, unsigned long attrs)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
static inline unsigned long iommu_dma_get_merge_boundary(struct device *dev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static inline size_t iommu_dma_opt_mapping_size(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static inline size_t iommu_dma_max_mapping_size(struct device *dev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static inline void iommu_dma_free(struct device *dev, size_t size,
|
||||||
|
void *cpu_addr, dma_addr_t handle, unsigned long attrs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
static inline dma_addr_t iommu_dma_map_resource(struct device *dev,
|
||||||
|
phys_addr_t phys, size_t size, enum dma_data_direction dir,
|
||||||
|
unsigned long attrs)
|
||||||
|
{
|
||||||
|
return DMA_MAPPING_ERROR;
|
||||||
|
}
|
||||||
|
static inline void iommu_dma_unmap_resource(struct device *dev,
|
||||||
|
dma_addr_t handle, size_t size, enum dma_data_direction dir,
|
||||||
|
unsigned long attrs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
static inline struct sg_table *
|
||||||
|
iommu_dma_alloc_noncontiguous(struct device *dev, size_t size,
|
||||||
|
enum dma_data_direction dir, gfp_t gfp, unsigned long attrs)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
static inline void iommu_dma_free_noncontiguous(struct device *dev, size_t size,
|
||||||
|
struct sg_table *sgt, enum dma_data_direction dir)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
static inline void iommu_dma_sync_single_for_cpu(struct device *dev,
|
||||||
|
dma_addr_t dma_handle, size_t size,
|
||||||
|
enum dma_data_direction dir)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
static inline void iommu_dma_sync_single_for_device(struct device *dev,
|
||||||
|
dma_addr_t dma_handle, size_t size, enum dma_data_direction dir)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
static inline void iommu_dma_sync_sg_for_cpu(struct device *dev,
|
||||||
|
struct scatterlist *sgl, int nelems,
|
||||||
|
enum dma_data_direction dir)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
static inline void iommu_dma_sync_sg_for_device(struct device *dev,
|
||||||
|
struct scatterlist *sgl, int nelems,
|
||||||
|
enum dma_data_direction dir)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_IOMMU_DMA */
|
||||||
|
#endif /* _LINUX_IOMMU_DMA_H */
|
@ -8,8 +8,12 @@ config HAS_DMA
|
|||||||
depends on !NO_DMA
|
depends on !NO_DMA
|
||||||
default y
|
default y
|
||||||
|
|
||||||
|
config DMA_OPS_HELPERS
|
||||||
|
bool
|
||||||
|
|
||||||
config DMA_OPS
|
config DMA_OPS
|
||||||
depends on HAS_DMA
|
depends on HAS_DMA
|
||||||
|
select DMA_OPS_HELPERS
|
||||||
bool
|
bool
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
obj-$(CONFIG_HAS_DMA) += mapping.o direct.o
|
obj-$(CONFIG_HAS_DMA) += mapping.o direct.o
|
||||||
obj-$(CONFIG_DMA_OPS) += ops_helpers.o
|
obj-$(CONFIG_DMA_OPS_HELPERS) += ops_helpers.o
|
||||||
obj-$(CONFIG_DMA_OPS) += dummy.o
|
obj-$(CONFIG_DMA_OPS) += dummy.o
|
||||||
obj-$(CONFIG_DMA_CMA) += contiguous.o
|
obj-$(CONFIG_DMA_CMA) += contiguous.o
|
||||||
obj-$(CONFIG_DMA_DECLARE_COHERENT) += coherent.o
|
obj-$(CONFIG_DMA_DECLARE_COHERENT) += coherent.o
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <linux/dma-map-ops.h>
|
#include <linux/dma-map-ops.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
|
#include <linux/iommu-dma.h>
|
||||||
#include <linux/kmsan.h>
|
#include <linux/kmsan.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
@ -113,11 +114,27 @@ void *dmam_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dmam_alloc_attrs);
|
EXPORT_SYMBOL(dmam_alloc_attrs);
|
||||||
|
|
||||||
|
#ifdef CONFIG_IOMMU_DMA
|
||||||
|
static bool use_dma_iommu(struct device *dev)
|
||||||
|
{
|
||||||
|
return dev->dma_iommu;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static bool use_dma_iommu(struct device *dev)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static bool dma_go_direct(struct device *dev, dma_addr_t mask,
|
static bool dma_go_direct(struct device *dev, dma_addr_t mask,
|
||||||
const struct dma_map_ops *ops)
|
const struct dma_map_ops *ops)
|
||||||
{
|
{
|
||||||
|
if (use_dma_iommu(dev))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (likely(!ops))
|
if (likely(!ops))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
#ifdef CONFIG_DMA_OPS_BYPASS
|
#ifdef CONFIG_DMA_OPS_BYPASS
|
||||||
if (dev->dma_ops_bypass)
|
if (dev->dma_ops_bypass)
|
||||||
return min_not_zero(mask, dev->bus_dma_limit) >=
|
return min_not_zero(mask, dev->bus_dma_limit) >=
|
||||||
@ -159,6 +176,8 @@ dma_addr_t dma_map_page_attrs(struct device *dev, struct page *page,
|
|||||||
if (dma_map_direct(dev, ops) ||
|
if (dma_map_direct(dev, ops) ||
|
||||||
arch_dma_map_page_direct(dev, page_to_phys(page) + offset + size))
|
arch_dma_map_page_direct(dev, page_to_phys(page) + offset + size))
|
||||||
addr = dma_direct_map_page(dev, page, offset, size, dir, attrs);
|
addr = dma_direct_map_page(dev, page, offset, size, dir, attrs);
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
addr = iommu_dma_map_page(dev, page, offset, size, dir, attrs);
|
||||||
else
|
else
|
||||||
addr = ops->map_page(dev, page, offset, size, dir, attrs);
|
addr = ops->map_page(dev, page, offset, size, dir, attrs);
|
||||||
kmsan_handle_dma(page, offset, size, dir);
|
kmsan_handle_dma(page, offset, size, dir);
|
||||||
@ -177,6 +196,8 @@ void dma_unmap_page_attrs(struct device *dev, dma_addr_t addr, size_t size,
|
|||||||
if (dma_map_direct(dev, ops) ||
|
if (dma_map_direct(dev, ops) ||
|
||||||
arch_dma_unmap_page_direct(dev, addr + size))
|
arch_dma_unmap_page_direct(dev, addr + size))
|
||||||
dma_direct_unmap_page(dev, addr, size, dir, attrs);
|
dma_direct_unmap_page(dev, addr, size, dir, attrs);
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
iommu_dma_unmap_page(dev, addr, size, dir, attrs);
|
||||||
else
|
else
|
||||||
ops->unmap_page(dev, addr, size, dir, attrs);
|
ops->unmap_page(dev, addr, size, dir, attrs);
|
||||||
debug_dma_unmap_page(dev, addr, size, dir);
|
debug_dma_unmap_page(dev, addr, size, dir);
|
||||||
@ -197,6 +218,8 @@ static int __dma_map_sg_attrs(struct device *dev, struct scatterlist *sg,
|
|||||||
if (dma_map_direct(dev, ops) ||
|
if (dma_map_direct(dev, ops) ||
|
||||||
arch_dma_map_sg_direct(dev, sg, nents))
|
arch_dma_map_sg_direct(dev, sg, nents))
|
||||||
ents = dma_direct_map_sg(dev, sg, nents, dir, attrs);
|
ents = dma_direct_map_sg(dev, sg, nents, dir, attrs);
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
ents = iommu_dma_map_sg(dev, sg, nents, dir, attrs);
|
||||||
else
|
else
|
||||||
ents = ops->map_sg(dev, sg, nents, dir, attrs);
|
ents = ops->map_sg(dev, sg, nents, dir, attrs);
|
||||||
|
|
||||||
@ -291,7 +314,9 @@ void dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg,
|
|||||||
if (dma_map_direct(dev, ops) ||
|
if (dma_map_direct(dev, ops) ||
|
||||||
arch_dma_unmap_sg_direct(dev, sg, nents))
|
arch_dma_unmap_sg_direct(dev, sg, nents))
|
||||||
dma_direct_unmap_sg(dev, sg, nents, dir, attrs);
|
dma_direct_unmap_sg(dev, sg, nents, dir, attrs);
|
||||||
else
|
else if (use_dma_iommu(dev))
|
||||||
|
iommu_dma_unmap_sg(dev, sg, nents, dir, attrs);
|
||||||
|
else if (ops->unmap_sg)
|
||||||
ops->unmap_sg(dev, sg, nents, dir, attrs);
|
ops->unmap_sg(dev, sg, nents, dir, attrs);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dma_unmap_sg_attrs);
|
EXPORT_SYMBOL(dma_unmap_sg_attrs);
|
||||||
@ -309,6 +334,8 @@ dma_addr_t dma_map_resource(struct device *dev, phys_addr_t phys_addr,
|
|||||||
|
|
||||||
if (dma_map_direct(dev, ops))
|
if (dma_map_direct(dev, ops))
|
||||||
addr = dma_direct_map_resource(dev, phys_addr, size, dir, attrs);
|
addr = dma_direct_map_resource(dev, phys_addr, size, dir, attrs);
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
addr = iommu_dma_map_resource(dev, phys_addr, size, dir, attrs);
|
||||||
else if (ops->map_resource)
|
else if (ops->map_resource)
|
||||||
addr = ops->map_resource(dev, phys_addr, size, dir, attrs);
|
addr = ops->map_resource(dev, phys_addr, size, dir, attrs);
|
||||||
|
|
||||||
@ -323,7 +350,11 @@ void dma_unmap_resource(struct device *dev, dma_addr_t addr, size_t size,
|
|||||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||||
|
|
||||||
BUG_ON(!valid_dma_direction(dir));
|
BUG_ON(!valid_dma_direction(dir));
|
||||||
if (!dma_map_direct(dev, ops) && ops->unmap_resource)
|
if (dma_map_direct(dev, ops))
|
||||||
|
; /* nothing to do: uncached and no swiotlb */
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
iommu_dma_unmap_resource(dev, addr, size, dir, attrs);
|
||||||
|
else if (ops->unmap_resource)
|
||||||
ops->unmap_resource(dev, addr, size, dir, attrs);
|
ops->unmap_resource(dev, addr, size, dir, attrs);
|
||||||
debug_dma_unmap_resource(dev, addr, size, dir);
|
debug_dma_unmap_resource(dev, addr, size, dir);
|
||||||
}
|
}
|
||||||
@ -338,6 +369,8 @@ void __dma_sync_single_for_cpu(struct device *dev, dma_addr_t addr, size_t size,
|
|||||||
BUG_ON(!valid_dma_direction(dir));
|
BUG_ON(!valid_dma_direction(dir));
|
||||||
if (dma_map_direct(dev, ops))
|
if (dma_map_direct(dev, ops))
|
||||||
dma_direct_sync_single_for_cpu(dev, addr, size, dir);
|
dma_direct_sync_single_for_cpu(dev, addr, size, dir);
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
iommu_dma_sync_single_for_cpu(dev, addr, size, dir);
|
||||||
else if (ops->sync_single_for_cpu)
|
else if (ops->sync_single_for_cpu)
|
||||||
ops->sync_single_for_cpu(dev, addr, size, dir);
|
ops->sync_single_for_cpu(dev, addr, size, dir);
|
||||||
debug_dma_sync_single_for_cpu(dev, addr, size, dir);
|
debug_dma_sync_single_for_cpu(dev, addr, size, dir);
|
||||||
@ -352,6 +385,8 @@ void __dma_sync_single_for_device(struct device *dev, dma_addr_t addr,
|
|||||||
BUG_ON(!valid_dma_direction(dir));
|
BUG_ON(!valid_dma_direction(dir));
|
||||||
if (dma_map_direct(dev, ops))
|
if (dma_map_direct(dev, ops))
|
||||||
dma_direct_sync_single_for_device(dev, addr, size, dir);
|
dma_direct_sync_single_for_device(dev, addr, size, dir);
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
iommu_dma_sync_single_for_device(dev, addr, size, dir);
|
||||||
else if (ops->sync_single_for_device)
|
else if (ops->sync_single_for_device)
|
||||||
ops->sync_single_for_device(dev, addr, size, dir);
|
ops->sync_single_for_device(dev, addr, size, dir);
|
||||||
debug_dma_sync_single_for_device(dev, addr, size, dir);
|
debug_dma_sync_single_for_device(dev, addr, size, dir);
|
||||||
@ -366,6 +401,8 @@ void __dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
|
|||||||
BUG_ON(!valid_dma_direction(dir));
|
BUG_ON(!valid_dma_direction(dir));
|
||||||
if (dma_map_direct(dev, ops))
|
if (dma_map_direct(dev, ops))
|
||||||
dma_direct_sync_sg_for_cpu(dev, sg, nelems, dir);
|
dma_direct_sync_sg_for_cpu(dev, sg, nelems, dir);
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
iommu_dma_sync_sg_for_cpu(dev, sg, nelems, dir);
|
||||||
else if (ops->sync_sg_for_cpu)
|
else if (ops->sync_sg_for_cpu)
|
||||||
ops->sync_sg_for_cpu(dev, sg, nelems, dir);
|
ops->sync_sg_for_cpu(dev, sg, nelems, dir);
|
||||||
debug_dma_sync_sg_for_cpu(dev, sg, nelems, dir);
|
debug_dma_sync_sg_for_cpu(dev, sg, nelems, dir);
|
||||||
@ -380,6 +417,8 @@ void __dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
|
|||||||
BUG_ON(!valid_dma_direction(dir));
|
BUG_ON(!valid_dma_direction(dir));
|
||||||
if (dma_map_direct(dev, ops))
|
if (dma_map_direct(dev, ops))
|
||||||
dma_direct_sync_sg_for_device(dev, sg, nelems, dir);
|
dma_direct_sync_sg_for_device(dev, sg, nelems, dir);
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
iommu_dma_sync_sg_for_device(dev, sg, nelems, dir);
|
||||||
else if (ops->sync_sg_for_device)
|
else if (ops->sync_sg_for_device)
|
||||||
ops->sync_sg_for_device(dev, sg, nelems, dir);
|
ops->sync_sg_for_device(dev, sg, nelems, dir);
|
||||||
debug_dma_sync_sg_for_device(dev, sg, nelems, dir);
|
debug_dma_sync_sg_for_device(dev, sg, nelems, dir);
|
||||||
@ -405,7 +444,7 @@ static void dma_setup_need_sync(struct device *dev)
|
|||||||
{
|
{
|
||||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||||
|
|
||||||
if (dma_map_direct(dev, ops) || (ops->flags & DMA_F_CAN_SKIP_SYNC))
|
if (dma_map_direct(dev, ops) || use_dma_iommu(dev))
|
||||||
/*
|
/*
|
||||||
* dma_skip_sync will be reset to %false on first SWIOTLB buffer
|
* dma_skip_sync will be reset to %false on first SWIOTLB buffer
|
||||||
* mapping, if any. During the device initialization, it's
|
* mapping, if any. During the device initialization, it's
|
||||||
@ -446,6 +485,9 @@ int dma_get_sgtable_attrs(struct device *dev, struct sg_table *sgt,
|
|||||||
if (dma_alloc_direct(dev, ops))
|
if (dma_alloc_direct(dev, ops))
|
||||||
return dma_direct_get_sgtable(dev, sgt, cpu_addr, dma_addr,
|
return dma_direct_get_sgtable(dev, sgt, cpu_addr, dma_addr,
|
||||||
size, attrs);
|
size, attrs);
|
||||||
|
if (use_dma_iommu(dev))
|
||||||
|
return iommu_dma_get_sgtable(dev, sgt, cpu_addr, dma_addr,
|
||||||
|
size, attrs);
|
||||||
if (!ops->get_sgtable)
|
if (!ops->get_sgtable)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
return ops->get_sgtable(dev, sgt, cpu_addr, dma_addr, size, attrs);
|
return ops->get_sgtable(dev, sgt, cpu_addr, dma_addr, size, attrs);
|
||||||
@ -482,6 +524,8 @@ bool dma_can_mmap(struct device *dev)
|
|||||||
|
|
||||||
if (dma_alloc_direct(dev, ops))
|
if (dma_alloc_direct(dev, ops))
|
||||||
return dma_direct_can_mmap(dev);
|
return dma_direct_can_mmap(dev);
|
||||||
|
if (use_dma_iommu(dev))
|
||||||
|
return true;
|
||||||
return ops->mmap != NULL;
|
return ops->mmap != NULL;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dma_can_mmap);
|
EXPORT_SYMBOL_GPL(dma_can_mmap);
|
||||||
@ -508,6 +552,9 @@ int dma_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
|
|||||||
if (dma_alloc_direct(dev, ops))
|
if (dma_alloc_direct(dev, ops))
|
||||||
return dma_direct_mmap(dev, vma, cpu_addr, dma_addr, size,
|
return dma_direct_mmap(dev, vma, cpu_addr, dma_addr, size,
|
||||||
attrs);
|
attrs);
|
||||||
|
if (use_dma_iommu(dev))
|
||||||
|
return iommu_dma_mmap(dev, vma, cpu_addr, dma_addr, size,
|
||||||
|
attrs);
|
||||||
if (!ops->mmap)
|
if (!ops->mmap)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
return ops->mmap(dev, vma, cpu_addr, dma_addr, size, attrs);
|
return ops->mmap(dev, vma, cpu_addr, dma_addr, size, attrs);
|
||||||
@ -559,6 +606,8 @@ void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
|
|||||||
|
|
||||||
if (dma_alloc_direct(dev, ops))
|
if (dma_alloc_direct(dev, ops))
|
||||||
cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs);
|
cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs);
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
cpu_addr = iommu_dma_alloc(dev, size, dma_handle, flag, attrs);
|
||||||
else if (ops->alloc)
|
else if (ops->alloc)
|
||||||
cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs);
|
cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs);
|
||||||
else
|
else
|
||||||
@ -591,6 +640,8 @@ void dma_free_attrs(struct device *dev, size_t size, void *cpu_addr,
|
|||||||
debug_dma_free_coherent(dev, size, cpu_addr, dma_handle);
|
debug_dma_free_coherent(dev, size, cpu_addr, dma_handle);
|
||||||
if (dma_alloc_direct(dev, ops))
|
if (dma_alloc_direct(dev, ops))
|
||||||
dma_direct_free(dev, size, cpu_addr, dma_handle, attrs);
|
dma_direct_free(dev, size, cpu_addr, dma_handle, attrs);
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
iommu_dma_free(dev, size, cpu_addr, dma_handle, attrs);
|
||||||
else if (ops->free)
|
else if (ops->free)
|
||||||
ops->free(dev, size, cpu_addr, dma_handle, attrs);
|
ops->free(dev, size, cpu_addr, dma_handle, attrs);
|
||||||
}
|
}
|
||||||
@ -611,6 +662,8 @@ static struct page *__dma_alloc_pages(struct device *dev, size_t size,
|
|||||||
size = PAGE_ALIGN(size);
|
size = PAGE_ALIGN(size);
|
||||||
if (dma_alloc_direct(dev, ops))
|
if (dma_alloc_direct(dev, ops))
|
||||||
return dma_direct_alloc_pages(dev, size, dma_handle, dir, gfp);
|
return dma_direct_alloc_pages(dev, size, dma_handle, dir, gfp);
|
||||||
|
if (use_dma_iommu(dev))
|
||||||
|
return dma_common_alloc_pages(dev, size, dma_handle, dir, gfp);
|
||||||
if (!ops->alloc_pages_op)
|
if (!ops->alloc_pages_op)
|
||||||
return NULL;
|
return NULL;
|
||||||
return ops->alloc_pages_op(dev, size, dma_handle, dir, gfp);
|
return ops->alloc_pages_op(dev, size, dma_handle, dir, gfp);
|
||||||
@ -635,6 +688,8 @@ static void __dma_free_pages(struct device *dev, size_t size, struct page *page,
|
|||||||
size = PAGE_ALIGN(size);
|
size = PAGE_ALIGN(size);
|
||||||
if (dma_alloc_direct(dev, ops))
|
if (dma_alloc_direct(dev, ops))
|
||||||
dma_direct_free_pages(dev, size, page, dma_handle, dir);
|
dma_direct_free_pages(dev, size, page, dma_handle, dir);
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
dma_common_free_pages(dev, size, page, dma_handle, dir);
|
||||||
else if (ops->free_pages)
|
else if (ops->free_pages)
|
||||||
ops->free_pages(dev, size, page, dma_handle, dir);
|
ops->free_pages(dev, size, page, dma_handle, dir);
|
||||||
}
|
}
|
||||||
@ -697,6 +752,8 @@ struct sg_table *dma_alloc_noncontiguous(struct device *dev, size_t size,
|
|||||||
|
|
||||||
if (ops && ops->alloc_noncontiguous)
|
if (ops && ops->alloc_noncontiguous)
|
||||||
sgt = ops->alloc_noncontiguous(dev, size, dir, gfp, attrs);
|
sgt = ops->alloc_noncontiguous(dev, size, dir, gfp, attrs);
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
sgt = iommu_dma_alloc_noncontiguous(dev, size, dir, gfp, attrs);
|
||||||
else
|
else
|
||||||
sgt = alloc_single_sgt(dev, size, dir, gfp);
|
sgt = alloc_single_sgt(dev, size, dir, gfp);
|
||||||
|
|
||||||
@ -725,6 +782,8 @@ void dma_free_noncontiguous(struct device *dev, size_t size,
|
|||||||
debug_dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir);
|
debug_dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir);
|
||||||
if (ops && ops->free_noncontiguous)
|
if (ops && ops->free_noncontiguous)
|
||||||
ops->free_noncontiguous(dev, size, sgt, dir);
|
ops->free_noncontiguous(dev, size, sgt, dir);
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
iommu_dma_free_noncontiguous(dev, size, sgt, dir);
|
||||||
else
|
else
|
||||||
free_single_sgt(dev, size, sgt, dir);
|
free_single_sgt(dev, size, sgt, dir);
|
||||||
}
|
}
|
||||||
@ -772,6 +831,8 @@ static int dma_supported(struct device *dev, u64 mask)
|
|||||||
{
|
{
|
||||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||||
|
|
||||||
|
if (WARN_ON(ops && use_dma_iommu(dev)))
|
||||||
|
return false;
|
||||||
/*
|
/*
|
||||||
* ->dma_supported sets the bypass flag, so we must always call
|
* ->dma_supported sets the bypass flag, so we must always call
|
||||||
* into the method here unless the device is truly direct mapped.
|
* into the method here unless the device is truly direct mapped.
|
||||||
@ -787,17 +848,14 @@ bool dma_pci_p2pdma_supported(struct device *dev)
|
|||||||
{
|
{
|
||||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||||
|
|
||||||
/* if ops is not set, dma direct will be used which supports P2PDMA */
|
|
||||||
if (!ops)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: dma_ops_bypass is not checked here because P2PDMA should
|
* Note: dma_ops_bypass is not checked here because P2PDMA should
|
||||||
* not be used with dma mapping ops that do not have support even
|
* not be used with dma mapping ops that do not have support even
|
||||||
* if the specific device is bypassing them.
|
* if the specific device is bypassing them.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return ops->flags & DMA_F_PCI_P2PDMA_SUPPORTED;
|
/* if ops is not set, dma direct and default IOMMU support P2PDMA */
|
||||||
|
return !ops;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dma_pci_p2pdma_supported);
|
EXPORT_SYMBOL_GPL(dma_pci_p2pdma_supported);
|
||||||
|
|
||||||
@ -865,6 +923,8 @@ size_t dma_max_mapping_size(struct device *dev)
|
|||||||
|
|
||||||
if (dma_map_direct(dev, ops))
|
if (dma_map_direct(dev, ops))
|
||||||
size = dma_direct_max_mapping_size(dev);
|
size = dma_direct_max_mapping_size(dev);
|
||||||
|
else if (use_dma_iommu(dev))
|
||||||
|
size = iommu_dma_max_mapping_size(dev);
|
||||||
else if (ops && ops->max_mapping_size)
|
else if (ops && ops->max_mapping_size)
|
||||||
size = ops->max_mapping_size(dev);
|
size = ops->max_mapping_size(dev);
|
||||||
|
|
||||||
@ -877,7 +937,9 @@ size_t dma_opt_mapping_size(struct device *dev)
|
|||||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||||
size_t size = SIZE_MAX;
|
size_t size = SIZE_MAX;
|
||||||
|
|
||||||
if (ops && ops->opt_mapping_size)
|
if (use_dma_iommu(dev))
|
||||||
|
size = iommu_dma_opt_mapping_size();
|
||||||
|
else if (ops && ops->opt_mapping_size)
|
||||||
size = ops->opt_mapping_size();
|
size = ops->opt_mapping_size();
|
||||||
|
|
||||||
return min(dma_max_mapping_size(dev), size);
|
return min(dma_max_mapping_size(dev), size);
|
||||||
@ -888,6 +950,9 @@ unsigned long dma_get_merge_boundary(struct device *dev)
|
|||||||
{
|
{
|
||||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||||
|
|
||||||
|
if (use_dma_iommu(dev))
|
||||||
|
return iommu_dma_get_merge_boundary(dev);
|
||||||
|
|
||||||
if (!ops || !ops->get_merge_boundary)
|
if (!ops || !ops->get_merge_boundary)
|
||||||
return 0; /* can't merge */
|
return 0; /* can't merge */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user