mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
Currently, arch_sync_dma_for_cpu and arch_sync_dma_for_device always wait for the completion of each DMA buffer. That is, issuing the DMA sync and waiting for completion is done in a single API call. For scatter-gather lists with multiple entries, this means issuing and waiting is repeated for each entry, which can hurt performance. Architectures like ARM64 may be able to issue all DMA sync operations for all entries first and then wait for completion together. To address this, arch_sync_dma_for_* now batches DMA operations and performs a flush afterward. On ARM64, the flush is implemented with a dsb instruction in arch_sync_dma_flush(). On other architectures, arch_sync_dma_flush() is currently a nop. Cc: Leon Romanovsky <leon@kernel.org> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Will Deacon <will@kernel.org> Cc: Marek Szyprowski <m.szyprowski@samsung.com> Cc: Robin Murphy <robin.murphy@arm.com> Cc: Ada Couprie Diaz <ada.coupriediaz@arm.com> Cc: Ard Biesheuvel <ardb@kernel.org> Cc: Marc Zyngier <maz@kernel.org> Cc: Anshuman Khandual <anshuman.khandual@arm.com> Cc: Ryan Roberts <ryan.roberts@arm.com> Cc: Suren Baghdasaryan <surenb@google.com> Cc: Joerg Roedel <joro@8bytes.org> Cc: Stefano Stabellini <sstabellini@kernel.org> Cc: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com> Cc: Tangquan Zheng <zhengtangquan@oppo.com> Reviewed-by: Juergen Gross <jgross@suse.com> # drivers/xen/swiotlb-xen.c Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com> Signed-off-by: Barry Song <baohua@kernel.org> Reviewed-by: Leon Romanovsky <leonro@nvidia.com> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Link: https://lore.kernel.org/r/20260228221316.59934-1-21cnbao@gmail.com
143 lines
4.2 KiB
C
143 lines
4.2 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (C) 2018 Christoph Hellwig.
|
|
*
|
|
* DMA operations that map physical memory directly without using an IOMMU.
|
|
*/
|
|
#ifndef _KERNEL_DMA_DIRECT_H
|
|
#define _KERNEL_DMA_DIRECT_H
|
|
|
|
#include <linux/dma-direct.h>
|
|
#include <linux/memremap.h>
|
|
|
|
int dma_direct_get_sgtable(struct device *dev, struct sg_table *sgt,
|
|
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
|
unsigned long attrs);
|
|
bool dma_direct_can_mmap(struct device *dev);
|
|
int dma_direct_mmap(struct device *dev, struct vm_area_struct *vma,
|
|
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
|
unsigned long attrs);
|
|
bool dma_direct_need_sync(struct device *dev, dma_addr_t dma_addr);
|
|
int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
|
|
enum dma_data_direction dir, unsigned long attrs);
|
|
bool dma_direct_all_ram_mapped(struct device *dev);
|
|
size_t dma_direct_max_mapping_size(struct device *dev);
|
|
|
|
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
|
|
defined(CONFIG_SWIOTLB)
|
|
void dma_direct_sync_sg_for_device(struct device *dev, struct scatterlist *sgl,
|
|
int nents, enum dma_data_direction dir);
|
|
#else
|
|
static inline void dma_direct_sync_sg_for_device(struct device *dev,
|
|
struct scatterlist *sgl, int nents, enum dma_data_direction dir)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
|
|
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) || \
|
|
defined(CONFIG_SWIOTLB)
|
|
void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sgl,
|
|
int nents, enum dma_data_direction dir, unsigned long attrs);
|
|
void dma_direct_sync_sg_for_cpu(struct device *dev,
|
|
struct scatterlist *sgl, int nents, enum dma_data_direction dir);
|
|
#else
|
|
static inline void dma_direct_unmap_sg(struct device *dev,
|
|
struct scatterlist *sgl, int nents, enum dma_data_direction dir,
|
|
unsigned long attrs)
|
|
{
|
|
}
|
|
static inline void dma_direct_sync_sg_for_cpu(struct device *dev,
|
|
struct scatterlist *sgl, int nents, enum dma_data_direction dir)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static inline void dma_direct_sync_single_for_device(struct device *dev,
|
|
dma_addr_t addr, size_t size, enum dma_data_direction dir)
|
|
{
|
|
phys_addr_t paddr = dma_to_phys(dev, addr);
|
|
|
|
swiotlb_sync_single_for_device(dev, paddr, size, dir);
|
|
|
|
if (!dev_is_dma_coherent(dev)) {
|
|
arch_sync_dma_for_device(paddr, size, dir);
|
|
arch_sync_dma_flush();
|
|
}
|
|
}
|
|
|
|
static inline void dma_direct_sync_single_for_cpu(struct device *dev,
|
|
dma_addr_t addr, size_t size, enum dma_data_direction dir)
|
|
{
|
|
phys_addr_t paddr = dma_to_phys(dev, addr);
|
|
|
|
if (!dev_is_dma_coherent(dev)) {
|
|
arch_sync_dma_for_cpu(paddr, size, dir);
|
|
arch_sync_dma_flush();
|
|
arch_sync_dma_for_cpu_all();
|
|
}
|
|
|
|
swiotlb_sync_single_for_cpu(dev, paddr, size, dir);
|
|
}
|
|
|
|
static inline dma_addr_t dma_direct_map_phys(struct device *dev,
|
|
phys_addr_t phys, size_t size, enum dma_data_direction dir,
|
|
unsigned long attrs)
|
|
{
|
|
dma_addr_t dma_addr;
|
|
|
|
if (is_swiotlb_force_bounce(dev)) {
|
|
if (attrs & DMA_ATTR_MMIO)
|
|
goto err_overflow;
|
|
|
|
return swiotlb_map(dev, phys, size, dir, attrs);
|
|
}
|
|
|
|
if (attrs & DMA_ATTR_MMIO) {
|
|
dma_addr = phys;
|
|
if (unlikely(!dma_capable(dev, dma_addr, size, false)))
|
|
goto err_overflow;
|
|
} else {
|
|
dma_addr = phys_to_dma(dev, phys);
|
|
if (unlikely(!dma_capable(dev, dma_addr, size, true)) ||
|
|
dma_kmalloc_needs_bounce(dev, size, dir)) {
|
|
if (is_swiotlb_active(dev))
|
|
return swiotlb_map(dev, phys, size, dir, attrs);
|
|
|
|
goto err_overflow;
|
|
}
|
|
}
|
|
|
|
if (!dev_is_dma_coherent(dev) &&
|
|
!(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO))) {
|
|
arch_sync_dma_for_device(phys, size, dir);
|
|
arch_sync_dma_flush();
|
|
}
|
|
return dma_addr;
|
|
|
|
err_overflow:
|
|
dev_WARN_ONCE(
|
|
dev, 1,
|
|
"DMA addr %pad+%zu overflow (mask %llx, bus limit %llx).\n",
|
|
&dma_addr, size, *dev->dma_mask, dev->bus_dma_limit);
|
|
return DMA_MAPPING_ERROR;
|
|
}
|
|
|
|
static inline void dma_direct_unmap_phys(struct device *dev, dma_addr_t addr,
|
|
size_t size, enum dma_data_direction dir, unsigned long attrs)
|
|
{
|
|
phys_addr_t phys;
|
|
|
|
if (attrs & DMA_ATTR_MMIO)
|
|
/* nothing to do: uncached and no swiotlb */
|
|
return;
|
|
|
|
phys = dma_to_phys(dev, addr);
|
|
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
|
|
dma_direct_sync_single_for_cpu(dev, addr, size, dir);
|
|
|
|
swiotlb_tbl_unmap_single(dev, phys, size, dir,
|
|
attrs | DMA_ATTR_SKIP_CPU_SYNC);
|
|
}
|
|
#endif /* _KERNEL_DMA_DIRECT_H */
|