Merge tag 'dma-mapping-5.10' of git://git.infradead.org/users/hch/dma-mapping
Pull dma-mapping updates from Christoph Hellwig: - rework the non-coherent DMA allocator - move private definitions out of <linux/dma-mapping.h> - lower CMA_ALIGNMENT (Paul Cercueil) - remove the omap1 dma address translation in favor of the common code - make dma-direct aware of multiple dma offset ranges (Jim Quinlan) - support per-node DMA CMA areas (Barry Song) - increase the default seg boundary limit (Nicolin Chen) - misc fixes (Robin Murphy, Thomas Tai, Xu Wang) - various cleanups * tag 'dma-mapping-5.10' of git://git.infradead.org/users/hch/dma-mapping: (63 commits) ARM/ixp4xx: add a missing include of dma-map-ops.h dma-direct: simplify the DMA_ATTR_NO_KERNEL_MAPPING handling dma-direct: factor out a dma_direct_alloc_from_pool helper dma-direct check for highmem pages in dma_direct_alloc_pages dma-mapping: merge <linux/dma-noncoherent.h> into <linux/dma-map-ops.h> dma-mapping: move large parts of <linux/dma-direct.h> to kernel/dma dma-mapping: move dma-debug.h to kernel/dma/ dma-mapping: remove <asm/dma-contiguous.h> dma-mapping: merge <linux/dma-contiguous.h> into <linux/dma-map-ops.h> dma-contiguous: remove dma_contiguous_set_default dma-contiguous: remove dev_set_cma_area dma-contiguous: remove dma_declare_contiguous dma-mapping: split <linux/dma-mapping.h> cma: decrease CMA_ALIGNMENT lower limit to 2 firewire-ohci: use dma_alloc_pages dma-iommu: implement ->alloc_noncoherent dma-mapping: add new {alloc,free}_noncoherent dma_map_ops methods dma-mapping: add a new dma_alloc_pages API dma-mapping: remove dma_cache_sync 53c700: convert to dma_alloc_noncoherent ...
This commit is contained in:
@@ -9,6 +9,7 @@ config HAS_DMA
|
||||
default y
|
||||
|
||||
config DMA_OPS
|
||||
depends on HAS_DMA
|
||||
bool
|
||||
|
||||
#
|
||||
@@ -43,6 +44,12 @@ config ARCH_HAS_DMA_SET_MASK
|
||||
config ARCH_HAS_DMA_WRITE_COMBINE
|
||||
bool
|
||||
|
||||
#
|
||||
# Select if the architectures provides the arch_dma_mark_clean hook
|
||||
#
|
||||
config ARCH_HAS_DMA_MARK_CLEAN
|
||||
bool
|
||||
|
||||
config DMA_DECLARE_COHERENT
|
||||
bool
|
||||
|
||||
@@ -68,9 +75,6 @@ config ARCH_HAS_DMA_PREP_COHERENT
|
||||
config ARCH_HAS_FORCE_DMA_UNENCRYPTED
|
||||
bool
|
||||
|
||||
config DMA_NONCOHERENT_CACHE_SYNC
|
||||
bool
|
||||
|
||||
config DMA_VIRT_OPS
|
||||
bool
|
||||
depends on HAS_DMA
|
||||
@@ -114,10 +118,21 @@ config DMA_CMA
|
||||
You can disable CMA by specifying "cma=0" on the kernel's command
|
||||
line.
|
||||
|
||||
For more information see <include/linux/dma-contiguous.h>.
|
||||
For more information see <kernel/dma/contiguous.c>.
|
||||
If unsure, say "n".
|
||||
|
||||
if DMA_CMA
|
||||
|
||||
config DMA_PERNUMA_CMA
|
||||
bool "Enable separate DMA Contiguous Memory Area for each NUMA Node"
|
||||
default NUMA && ARM64
|
||||
help
|
||||
Enable this option to get pernuma CMA areas so that devices like
|
||||
ARM64 SMMU can get local memory by DMA coherent APIs.
|
||||
|
||||
You can set the size of pernuma CMA by specifying "cma_pernuma=size"
|
||||
on the kernel's command line.
|
||||
|
||||
comment "Default contiguous memory area size:"
|
||||
|
||||
config CMA_SIZE_MBYTES
|
||||
@@ -162,7 +177,7 @@ endchoice
|
||||
|
||||
config CMA_ALIGNMENT
|
||||
int "Maximum PAGE_SIZE order of alignment for contiguous buffers"
|
||||
range 4 12
|
||||
range 2 12
|
||||
default 8
|
||||
help
|
||||
DMA mapping framework by default aligns all buffers to the smallest
|
||||
|
@@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_HAS_DMA) += mapping.o direct.o
|
||||
obj-$(CONFIG_DMA_OPS) += ops_helpers.o
|
||||
obj-$(CONFIG_DMA_OPS) += dummy.o
|
||||
obj-$(CONFIG_DMA_CMA) += contiguous.o
|
||||
obj-$(CONFIG_DMA_DECLARE_COHERENT) += coherent.o
|
||||
|
@@ -7,7 +7,8 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
|
||||
struct dma_coherent_mem {
|
||||
void *virt_base;
|
||||
@@ -32,9 +33,8 @@ static inline dma_addr_t dma_get_device_base(struct device *dev,
|
||||
struct dma_coherent_mem * mem)
|
||||
{
|
||||
if (mem->use_dev_dma_pfn_offset)
|
||||
return (mem->pfn_base - dev->dma_pfn_offset) << PAGE_SHIFT;
|
||||
else
|
||||
return mem->device_base;
|
||||
return phys_to_dma(dev, PFN_PHYS(mem->pfn_base));
|
||||
return mem->device_base;
|
||||
}
|
||||
|
||||
static int dma_init_coherent_memory(phys_addr_t phys_addr,
|
||||
@@ -107,6 +107,23 @@ static int dma_assign_coherent_memory(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Declare a region of memory to be handed out by dma_alloc_coherent() when it
|
||||
* is asked for coherent memory for this device. This shall only be used
|
||||
* from platform code, usually based on the device tree description.
|
||||
*
|
||||
* phys_addr is the CPU physical address to which the memory is currently
|
||||
* assigned (this will be ioremapped so the CPU can access the region).
|
||||
*
|
||||
* device_addr is the DMA address the device needs to be programmed with to
|
||||
* actually address this memory (this will be handed out as the dma_addr_t in
|
||||
* dma_alloc_coherent()).
|
||||
*
|
||||
* size is the size of the area (must be a multiple of PAGE_SIZE).
|
||||
*
|
||||
* As a simplification for the platforms, only *one* such region of memory may
|
||||
* be declared per device.
|
||||
*/
|
||||
int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr,
|
||||
dma_addr_t device_addr, size_t size)
|
||||
{
|
||||
|
@@ -5,6 +5,34 @@
|
||||
* Written by:
|
||||
* Marek Szyprowski <m.szyprowski@samsung.com>
|
||||
* Michal Nazarewicz <mina86@mina86.com>
|
||||
*
|
||||
* Contiguous Memory Allocator
|
||||
*
|
||||
* The Contiguous Memory Allocator (CMA) makes it possible to
|
||||
* allocate big contiguous chunks of memory after the system has
|
||||
* booted.
|
||||
*
|
||||
* Why is it needed?
|
||||
*
|
||||
* Various devices on embedded systems have no scatter-getter and/or
|
||||
* IO map support and require contiguous blocks of memory to
|
||||
* operate. They include devices such as cameras, hardware video
|
||||
* coders, etc.
|
||||
*
|
||||
* Such devices often require big memory buffers (a full HD frame
|
||||
* is, for instance, more then 2 mega pixels large, i.e. more than 6
|
||||
* MB of memory), which makes mechanisms such as kmalloc() or
|
||||
* alloc_page() ineffective.
|
||||
*
|
||||
* At the same time, a solution where a big memory region is
|
||||
* reserved for a device is suboptimal since often more memory is
|
||||
* reserved then strictly required and, moreover, the memory is
|
||||
* inaccessible to page system even if device drivers don't use it.
|
||||
*
|
||||
* CMA tries to solve this issue by operating on memory regions
|
||||
* where only movable pages can be allocated from. This way, kernel
|
||||
* can use the memory for pagecache and when device driver requests
|
||||
* it, allocated pages can be migrated.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "cma: " fmt
|
||||
@@ -16,12 +44,11 @@
|
||||
#endif
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/dma-contiguous.h>
|
||||
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/cma.h>
|
||||
|
||||
#ifdef CONFIG_CMA_SIZE_MBYTES
|
||||
@@ -69,6 +96,19 @@ static int __init early_cma(char *p)
|
||||
}
|
||||
early_param("cma", early_cma);
|
||||
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
|
||||
static struct cma *dma_contiguous_pernuma_area[MAX_NUMNODES];
|
||||
static phys_addr_t pernuma_size_bytes __initdata;
|
||||
|
||||
static int __init early_cma_pernuma(char *p)
|
||||
{
|
||||
pernuma_size_bytes = memparse(p, &p);
|
||||
return 0;
|
||||
}
|
||||
early_param("cma_pernuma", early_cma_pernuma);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CMA_SIZE_PERCENTAGE
|
||||
|
||||
static phys_addr_t __init __maybe_unused cma_early_percent_memory(void)
|
||||
@@ -87,6 +127,34 @@ static inline __maybe_unused phys_addr_t cma_early_percent_memory(void)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
void __init dma_pernuma_cma_reserve(void)
|
||||
{
|
||||
int nid;
|
||||
|
||||
if (!pernuma_size_bytes)
|
||||
return;
|
||||
|
||||
for_each_online_node(nid) {
|
||||
int ret;
|
||||
char name[CMA_MAX_NAME];
|
||||
struct cma **cma = &dma_contiguous_pernuma_area[nid];
|
||||
|
||||
snprintf(name, sizeof(name), "pernuma%d", nid);
|
||||
ret = cma_declare_contiguous_nid(0, pernuma_size_bytes, 0, 0,
|
||||
0, false, name, cma, nid);
|
||||
if (ret) {
|
||||
pr_warn("%s: reservation failed: err %d, node %d", __func__,
|
||||
ret, nid);
|
||||
continue;
|
||||
}
|
||||
|
||||
pr_debug("%s: reserved %llu MiB on node %d\n", __func__,
|
||||
(unsigned long long)pernuma_size_bytes / SZ_1M, nid);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* dma_contiguous_reserve() - reserve area(s) for contiguous memory handling
|
||||
* @limit: End address of the reserved memory (optional, 0 for any).
|
||||
@@ -134,6 +202,11 @@ void __init dma_contiguous_reserve(phys_addr_t limit)
|
||||
}
|
||||
}
|
||||
|
||||
void __weak
|
||||
dma_contiguous_early_fixup(phys_addr_t base, unsigned long size)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* dma_contiguous_reserve_area() - reserve custom contiguous area
|
||||
* @size: Size of the reserved area (in bytes),
|
||||
@@ -219,23 +292,44 @@ static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)
|
||||
* @size: Requested allocation size.
|
||||
* @gfp: Allocation flags.
|
||||
*
|
||||
* This function allocates contiguous memory buffer for specified device. It
|
||||
* tries to use device specific contiguous memory area if available, or the
|
||||
* default global one.
|
||||
* tries to use device specific contiguous memory area if available, or it
|
||||
* tries to use per-numa cma, if the allocation fails, it will fallback to
|
||||
* try default global one.
|
||||
*
|
||||
* Note that it byapss one-page size of allocations from the global area as
|
||||
* the addresses within one page are always contiguous, so there is no need
|
||||
* to waste CMA pages for that kind; it also helps reduce fragmentations.
|
||||
* Note that it bypass one-page size of allocations from the per-numa and
|
||||
* global area as the addresses within one page are always contiguous, so
|
||||
* there is no need to waste CMA pages for that kind; it also helps reduce
|
||||
* fragmentations.
|
||||
*/
|
||||
struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
|
||||
{
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
int nid = dev_to_node(dev);
|
||||
#endif
|
||||
|
||||
/* CMA can be used only in the context which permits sleeping */
|
||||
if (!gfpflags_allow_blocking(gfp))
|
||||
return NULL;
|
||||
if (dev->cma_area)
|
||||
return cma_alloc_aligned(dev->cma_area, size, gfp);
|
||||
if (size <= PAGE_SIZE || !dma_contiguous_default_area)
|
||||
if (size <= PAGE_SIZE)
|
||||
return NULL;
|
||||
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
if (nid != NUMA_NO_NODE && !(gfp & (GFP_DMA | GFP_DMA32))) {
|
||||
struct cma *cma = dma_contiguous_pernuma_area[nid];
|
||||
struct page *page;
|
||||
|
||||
if (cma) {
|
||||
page = cma_alloc_aligned(cma, size, gfp);
|
||||
if (page)
|
||||
return page;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!dma_contiguous_default_area)
|
||||
return NULL;
|
||||
|
||||
return cma_alloc_aligned(dma_contiguous_default_area, size, gfp);
|
||||
}
|
||||
|
||||
@@ -252,9 +346,27 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
|
||||
*/
|
||||
void dma_free_contiguous(struct device *dev, struct page *page, size_t size)
|
||||
{
|
||||
if (!cma_release(dev_get_cma_area(dev), page,
|
||||
PAGE_ALIGN(size) >> PAGE_SHIFT))
|
||||
__free_pages(page, get_order(size));
|
||||
unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
|
||||
/* if dev has its own cma, free page from there */
|
||||
if (dev->cma_area) {
|
||||
if (cma_release(dev->cma_area, page, count))
|
||||
return;
|
||||
} else {
|
||||
/*
|
||||
* otherwise, page is from either per-numa cma or default cma
|
||||
*/
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
if (cma_release(dma_contiguous_pernuma_area[page_to_nid(page)],
|
||||
page, count))
|
||||
return;
|
||||
#endif
|
||||
if (cma_release(dma_contiguous_default_area, page, count))
|
||||
return;
|
||||
}
|
||||
|
||||
/* not in any cma, free from buddy */
|
||||
__free_pages(page, get_order(size));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -270,14 +382,14 @@ void dma_free_contiguous(struct device *dev, struct page *page, size_t size)
|
||||
|
||||
static int rmem_cma_device_init(struct reserved_mem *rmem, struct device *dev)
|
||||
{
|
||||
dev_set_cma_area(dev, rmem->priv);
|
||||
dev->cma_area = rmem->priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmem_cma_device_release(struct reserved_mem *rmem,
|
||||
struct device *dev)
|
||||
{
|
||||
dev_set_cma_area(dev, NULL);
|
||||
dev->cma_area = NULL;
|
||||
}
|
||||
|
||||
static const struct reserved_mem_ops rmem_cma_ops = {
|
||||
@@ -318,7 +430,7 @@ static int __init rmem_cma_setup(struct reserved_mem *rmem)
|
||||
dma_contiguous_early_fixup(rmem->base, rmem->size);
|
||||
|
||||
if (default_cma)
|
||||
dma_contiguous_set_default(cma);
|
||||
dma_contiguous_default_area = cma;
|
||||
|
||||
rmem->ops = &rmem_cma_ops;
|
||||
rmem->priv = cma;
|
||||
|
@@ -9,10 +9,9 @@
|
||||
|
||||
#include <linux/sched/task_stack.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/sched/task.h>
|
||||
#include <linux/stacktrace.h>
|
||||
#include <linux/dma-debug.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/debugfs.h>
|
||||
@@ -24,8 +23,8 @@
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/sections.h>
|
||||
#include "debug.h"
|
||||
|
||||
#define HASH_SIZE 16384ULL
|
||||
#define HASH_FN_SHIFT 13
|
||||
@@ -1219,7 +1218,7 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset,
|
||||
entry->dev = dev;
|
||||
entry->type = dma_debug_single;
|
||||
entry->pfn = page_to_pfn(page);
|
||||
entry->offset = offset,
|
||||
entry->offset = offset;
|
||||
entry->dev_addr = dma_addr;
|
||||
entry->size = size;
|
||||
entry->direction = direction;
|
||||
@@ -1235,7 +1234,6 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset,
|
||||
|
||||
add_dma_entry(entry);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_map_page);
|
||||
|
||||
void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
|
||||
{
|
||||
@@ -1290,7 +1288,6 @@ void debug_dma_unmap_page(struct device *dev, dma_addr_t addr,
|
||||
return;
|
||||
check_unmap(&ref);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_unmap_page);
|
||||
|
||||
void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
|
||||
int nents, int mapped_ents, int direction)
|
||||
@@ -1310,7 +1307,7 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
|
||||
entry->type = dma_debug_sg;
|
||||
entry->dev = dev;
|
||||
entry->pfn = page_to_pfn(sg_page(s));
|
||||
entry->offset = s->offset,
|
||||
entry->offset = s->offset;
|
||||
entry->size = sg_dma_len(s);
|
||||
entry->dev_addr = sg_dma_address(s);
|
||||
entry->direction = direction;
|
||||
@@ -1328,7 +1325,6 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
|
||||
add_dma_entry(entry);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_map_sg);
|
||||
|
||||
static int get_nr_mapped_entries(struct device *dev,
|
||||
struct dma_debug_entry *ref)
|
||||
@@ -1380,7 +1376,6 @@ void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
|
||||
check_unmap(&ref);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_unmap_sg);
|
||||
|
||||
void debug_dma_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t dma_addr, void *virt)
|
||||
@@ -1466,7 +1461,6 @@ void debug_dma_map_resource(struct device *dev, phys_addr_t addr, size_t size,
|
||||
|
||||
add_dma_entry(entry);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_map_resource);
|
||||
|
||||
void debug_dma_unmap_resource(struct device *dev, dma_addr_t dma_addr,
|
||||
size_t size, int direction)
|
||||
@@ -1484,7 +1478,6 @@ void debug_dma_unmap_resource(struct device *dev, dma_addr_t dma_addr,
|
||||
|
||||
check_unmap(&ref);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_unmap_resource);
|
||||
|
||||
void debug_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
|
||||
size_t size, int direction)
|
||||
@@ -1503,7 +1496,6 @@ void debug_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
|
||||
|
||||
check_sync(dev, &ref, true);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_sync_single_for_cpu);
|
||||
|
||||
void debug_dma_sync_single_for_device(struct device *dev,
|
||||
dma_addr_t dma_handle, size_t size,
|
||||
@@ -1523,7 +1515,6 @@ void debug_dma_sync_single_for_device(struct device *dev,
|
||||
|
||||
check_sync(dev, &ref, false);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_sync_single_for_device);
|
||||
|
||||
void debug_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
|
||||
int nelems, int direction)
|
||||
@@ -1556,7 +1547,6 @@ void debug_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
|
||||
check_sync(dev, &ref, true);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_sync_sg_for_cpu);
|
||||
|
||||
void debug_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
|
||||
int nelems, int direction)
|
||||
@@ -1588,7 +1578,6 @@ void debug_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
|
||||
check_sync(dev, &ref, false);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_sync_sg_for_device);
|
||||
|
||||
static int __init dma_debug_driver_setup(char *str)
|
||||
{
|
||||
|
122
kernel/dma/debug.h
Normal file
122
kernel/dma/debug.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2008 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Author: Joerg Roedel <joerg.roedel@amd.com>
|
||||
*/
|
||||
|
||||
#ifndef _KERNEL_DMA_DEBUG_H
|
||||
#define _KERNEL_DMA_DEBUG_H
|
||||
|
||||
#ifdef CONFIG_DMA_API_DEBUG
|
||||
extern void debug_dma_map_page(struct device *dev, struct page *page,
|
||||
size_t offset, size_t size,
|
||||
int direction, dma_addr_t dma_addr);
|
||||
|
||||
extern void debug_dma_unmap_page(struct device *dev, dma_addr_t addr,
|
||||
size_t size, int direction);
|
||||
|
||||
extern void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
|
||||
int nents, int mapped_ents, int direction);
|
||||
|
||||
extern void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
|
||||
int nelems, int dir);
|
||||
|
||||
extern void debug_dma_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t dma_addr, void *virt);
|
||||
|
||||
extern void debug_dma_free_coherent(struct device *dev, size_t size,
|
||||
void *virt, dma_addr_t addr);
|
||||
|
||||
extern void debug_dma_map_resource(struct device *dev, phys_addr_t addr,
|
||||
size_t size, int direction,
|
||||
dma_addr_t dma_addr);
|
||||
|
||||
extern void debug_dma_unmap_resource(struct device *dev, dma_addr_t dma_addr,
|
||||
size_t size, int direction);
|
||||
|
||||
extern void debug_dma_sync_single_for_cpu(struct device *dev,
|
||||
dma_addr_t dma_handle, size_t size,
|
||||
int direction);
|
||||
|
||||
extern void debug_dma_sync_single_for_device(struct device *dev,
|
||||
dma_addr_t dma_handle,
|
||||
size_t size, int direction);
|
||||
|
||||
extern void debug_dma_sync_sg_for_cpu(struct device *dev,
|
||||
struct scatterlist *sg,
|
||||
int nelems, int direction);
|
||||
|
||||
extern void debug_dma_sync_sg_for_device(struct device *dev,
|
||||
struct scatterlist *sg,
|
||||
int nelems, int direction);
|
||||
#else /* CONFIG_DMA_API_DEBUG */
|
||||
static inline void debug_dma_map_page(struct device *dev, struct page *page,
|
||||
size_t offset, size_t size,
|
||||
int direction, dma_addr_t dma_addr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_unmap_page(struct device *dev, dma_addr_t addr,
|
||||
size_t size, int direction)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
|
||||
int nents, int mapped_ents, int direction)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_unmap_sg(struct device *dev,
|
||||
struct scatterlist *sglist,
|
||||
int nelems, int dir)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t dma_addr, void *virt)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_free_coherent(struct device *dev, size_t size,
|
||||
void *virt, dma_addr_t addr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_map_resource(struct device *dev, phys_addr_t addr,
|
||||
size_t size, int direction,
|
||||
dma_addr_t dma_addr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_unmap_resource(struct device *dev,
|
||||
dma_addr_t dma_addr, size_t size,
|
||||
int direction)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_sync_single_for_cpu(struct device *dev,
|
||||
dma_addr_t dma_handle,
|
||||
size_t size, int direction)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_sync_single_for_device(struct device *dev,
|
||||
dma_addr_t dma_handle,
|
||||
size_t size, int direction)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_sync_sg_for_cpu(struct device *dev,
|
||||
struct scatterlist *sg,
|
||||
int nelems, int direction)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_sync_sg_for_device(struct device *dev,
|
||||
struct scatterlist *sg,
|
||||
int nelems, int direction)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_DMA_API_DEBUG */
|
||||
#endif /* _KERNEL_DMA_DEBUG_H */
|
@@ -1,18 +1,19 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018 Christoph Hellwig.
|
||||
* Copyright (C) 2018-2020 Christoph Hellwig.
|
||||
*
|
||||
* DMA operations that map physical memory directly without using an IOMMU.
|
||||
*/
|
||||
#include <linux/memblock.h> /* for max_pfn */
|
||||
#include <linux/export.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#include <linux/pfn.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/set_memory.h>
|
||||
#include <linux/slab.h>
|
||||
#include "direct.h"
|
||||
|
||||
/*
|
||||
* Most architectures use ZONE_DMA for the first 16 Megabytes, but some use it
|
||||
@@ -25,7 +26,7 @@ static inline dma_addr_t phys_to_dma_direct(struct device *dev,
|
||||
phys_addr_t phys)
|
||||
{
|
||||
if (force_dma_unencrypted(dev))
|
||||
return __phys_to_dma(dev, phys);
|
||||
return phys_to_dma_unencrypted(dev, phys);
|
||||
return phys_to_dma(dev, phys);
|
||||
}
|
||||
|
||||
@@ -48,11 +49,6 @@ static gfp_t dma_direct_optimal_gfp_mask(struct device *dev, u64 dma_mask,
|
||||
{
|
||||
u64 dma_limit = min_not_zero(dma_mask, dev->bus_dma_limit);
|
||||
|
||||
if (force_dma_unencrypted(dev))
|
||||
*phys_limit = __dma_to_phys(dev, dma_limit);
|
||||
else
|
||||
*phys_limit = dma_to_phys(dev, dma_limit);
|
||||
|
||||
/*
|
||||
* Optimistically try the zone that the physical address mask falls
|
||||
* into first. If that returns memory that isn't actually addressable
|
||||
@@ -61,6 +57,7 @@ static gfp_t dma_direct_optimal_gfp_mask(struct device *dev, u64 dma_mask,
|
||||
* Note that GFP_DMA32 and GFP_DMA are no ops without the corresponding
|
||||
* zones.
|
||||
*/
|
||||
*phys_limit = dma_to_phys(dev, dma_limit);
|
||||
if (*phys_limit <= DMA_BIT_MASK(zone_dma_bits))
|
||||
return GFP_DMA;
|
||||
if (*phys_limit <= DMA_BIT_MASK(32))
|
||||
@@ -70,45 +67,16 @@ static gfp_t dma_direct_optimal_gfp_mask(struct device *dev, u64 dma_mask,
|
||||
|
||||
static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
|
||||
{
|
||||
return phys_to_dma_direct(dev, phys) + size - 1 <=
|
||||
min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
|
||||
}
|
||||
dma_addr_t dma_addr = phys_to_dma_direct(dev, phys);
|
||||
|
||||
/*
|
||||
* Decrypting memory is allowed to block, so if this device requires
|
||||
* unencrypted memory it must come from atomic pools.
|
||||
*/
|
||||
static inline bool dma_should_alloc_from_pool(struct device *dev, gfp_t gfp,
|
||||
unsigned long attrs)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_DMA_COHERENT_POOL))
|
||||
if (dma_addr == DMA_MAPPING_ERROR)
|
||||
return false;
|
||||
if (gfpflags_allow_blocking(gfp))
|
||||
return false;
|
||||
if (force_dma_unencrypted(dev))
|
||||
return true;
|
||||
if (!IS_ENABLED(CONFIG_DMA_DIRECT_REMAP))
|
||||
return false;
|
||||
if (dma_alloc_need_uncached(dev, attrs))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool dma_should_free_from_pool(struct device *dev,
|
||||
unsigned long attrs)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL))
|
||||
return true;
|
||||
if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) &&
|
||||
!force_dma_unencrypted(dev))
|
||||
return false;
|
||||
if (IS_ENABLED(CONFIG_DMA_DIRECT_REMAP))
|
||||
return true;
|
||||
return false;
|
||||
return dma_addr + size - 1 <=
|
||||
min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
|
||||
}
|
||||
|
||||
static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
gfp_t gfp, unsigned long attrs)
|
||||
gfp_t gfp)
|
||||
{
|
||||
int node = dev_to_node(dev);
|
||||
struct page *page = NULL;
|
||||
@@ -116,11 +84,6 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
|
||||
WARN_ON_ONCE(!PAGE_ALIGNED(size));
|
||||
|
||||
if (attrs & DMA_ATTR_NO_WARN)
|
||||
gfp |= __GFP_NOWARN;
|
||||
|
||||
/* we always manually zero the memory once we are done: */
|
||||
gfp &= ~__GFP_ZERO;
|
||||
gfp |= dma_direct_optimal_gfp_mask(dev, dev->coherent_dma_mask,
|
||||
&phys_limit);
|
||||
page = dma_alloc_contiguous(dev, size, gfp);
|
||||
@@ -151,7 +114,23 @@ again:
|
||||
return page;
|
||||
}
|
||||
|
||||
void *dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
static void *dma_direct_alloc_from_pool(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t gfp)
|
||||
{
|
||||
struct page *page;
|
||||
u64 phys_mask;
|
||||
void *ret;
|
||||
|
||||
gfp |= dma_direct_optimal_gfp_mask(dev, dev->coherent_dma_mask,
|
||||
&phys_mask);
|
||||
page = dma_alloc_from_pool(dev, size, &ret, gfp, dma_coherent_ok);
|
||||
if (!page)
|
||||
return NULL;
|
||||
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *dma_direct_alloc(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
|
||||
{
|
||||
struct page *page;
|
||||
@@ -159,35 +138,44 @@ void *dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
int err;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
if (dma_should_alloc_from_pool(dev, gfp, attrs)) {
|
||||
u64 phys_mask;
|
||||
|
||||
gfp |= dma_direct_optimal_gfp_mask(dev, dev->coherent_dma_mask,
|
||||
&phys_mask);
|
||||
page = dma_alloc_from_pool(dev, size, &ret, gfp,
|
||||
dma_coherent_ok);
|
||||
if (!page)
|
||||
return NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
page = __dma_direct_alloc_pages(dev, size, gfp, attrs);
|
||||
if (!page)
|
||||
return NULL;
|
||||
if (attrs & DMA_ATTR_NO_WARN)
|
||||
gfp |= __GFP_NOWARN;
|
||||
|
||||
if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) &&
|
||||
!force_dma_unencrypted(dev)) {
|
||||
page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO);
|
||||
if (!page)
|
||||
return NULL;
|
||||
/* remove any dirty cache lines on the kernel alias */
|
||||
if (!PageHighMem(page))
|
||||
arch_dma_prep_coherent(page, size);
|
||||
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
|
||||
/* return the page pointer as the opaque cookie */
|
||||
ret = page;
|
||||
goto done;
|
||||
return page;
|
||||
}
|
||||
|
||||
if (!IS_ENABLED(CONFIG_ARCH_HAS_DMA_SET_UNCACHED) &&
|
||||
!IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
|
||||
!dev_is_dma_coherent(dev))
|
||||
return arch_dma_alloc(dev, size, dma_handle, gfp, attrs);
|
||||
|
||||
/*
|
||||
* Remapping or decrypting memory may block. If either is required and
|
||||
* we can't block, allocate the memory from the atomic pools.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
|
||||
!gfpflags_allow_blocking(gfp) &&
|
||||
(force_dma_unencrypted(dev) ||
|
||||
(IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) && !dev_is_dma_coherent(dev))))
|
||||
return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);
|
||||
|
||||
/* we always manually zero the memory once we are done */
|
||||
page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO);
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
if ((IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
|
||||
dma_alloc_need_uncached(dev, attrs)) ||
|
||||
!dev_is_dma_coherent(dev)) ||
|
||||
(IS_ENABLED(CONFIG_DMA_REMAP) && PageHighMem(page))) {
|
||||
/* remove any dirty cache lines on the kernel alias */
|
||||
arch_dma_prep_coherent(page, size);
|
||||
@@ -230,17 +218,14 @@ void *dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
memset(ret, 0, size);
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARCH_HAS_DMA_SET_UNCACHED) &&
|
||||
dma_alloc_need_uncached(dev, attrs)) {
|
||||
!dev_is_dma_coherent(dev)) {
|
||||
arch_dma_prep_coherent(page, size);
|
||||
ret = arch_dma_set_uncached(ret, size);
|
||||
if (IS_ERR(ret))
|
||||
goto out_encrypt_pages;
|
||||
}
|
||||
done:
|
||||
if (force_dma_unencrypted(dev))
|
||||
*dma_handle = __phys_to_dma(dev, page_to_phys(page));
|
||||
else
|
||||
*dma_handle = phys_to_dma(dev, page_to_phys(page));
|
||||
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
|
||||
return ret;
|
||||
|
||||
out_encrypt_pages:
|
||||
@@ -256,16 +241,11 @@ out_free_pages:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dma_direct_free_pages(struct device *dev, size_t size, void *cpu_addr,
|
||||
dma_addr_t dma_addr, unsigned long attrs)
|
||||
void dma_direct_free(struct device *dev, size_t size,
|
||||
void *cpu_addr, dma_addr_t dma_addr, unsigned long attrs)
|
||||
{
|
||||
unsigned int page_order = get_order(size);
|
||||
|
||||
/* If cpu_addr is not from an atomic pool, dma_free_from_pool() fails */
|
||||
if (dma_should_free_from_pool(dev, attrs) &&
|
||||
dma_free_from_pool(dev, cpu_addr, PAGE_ALIGN(size)))
|
||||
return;
|
||||
|
||||
if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) &&
|
||||
!force_dma_unencrypted(dev)) {
|
||||
/* cpu_addr is a struct page cookie, not a kernel address */
|
||||
@@ -273,6 +253,18 @@ void dma_direct_free_pages(struct device *dev, size_t size, void *cpu_addr,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IS_ENABLED(CONFIG_ARCH_HAS_DMA_SET_UNCACHED) &&
|
||||
!IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
|
||||
!dev_is_dma_coherent(dev)) {
|
||||
arch_dma_free(dev, size, cpu_addr, dma_addr, attrs);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If cpu_addr is not from an atomic pool, dma_free_from_pool() fails */
|
||||
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
|
||||
dma_free_from_pool(dev, cpu_addr, PAGE_ALIGN(size)))
|
||||
return;
|
||||
|
||||
if (force_dma_unencrypted(dev))
|
||||
set_memory_encrypted((unsigned long)cpu_addr, 1 << page_order);
|
||||
|
||||
@@ -284,25 +276,60 @@ void dma_direct_free_pages(struct device *dev, size_t size, void *cpu_addr,
|
||||
dma_free_contiguous(dev, dma_direct_to_page(dev, dma_addr), size);
|
||||
}
|
||||
|
||||
void *dma_direct_alloc(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
|
||||
struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_ARCH_HAS_DMA_SET_UNCACHED) &&
|
||||
!IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
|
||||
dma_alloc_need_uncached(dev, attrs))
|
||||
return arch_dma_alloc(dev, size, dma_handle, gfp, attrs);
|
||||
return dma_direct_alloc_pages(dev, size, dma_handle, gfp, attrs);
|
||||
struct page *page;
|
||||
void *ret;
|
||||
|
||||
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
|
||||
force_dma_unencrypted(dev) && !gfpflags_allow_blocking(gfp))
|
||||
return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);
|
||||
|
||||
page = __dma_direct_alloc_pages(dev, size, gfp);
|
||||
if (!page)
|
||||
return NULL;
|
||||
if (PageHighMem(page)) {
|
||||
/*
|
||||
* Depending on the cma= arguments and per-arch setup
|
||||
* dma_alloc_contiguous could return highmem pages.
|
||||
* Without remapping there is no way to return them here,
|
||||
* so log an error and fail.
|
||||
*/
|
||||
dev_info(dev, "Rejecting highmem page from CMA.\n");
|
||||
goto out_free_pages;
|
||||
}
|
||||
|
||||
ret = page_address(page);
|
||||
if (force_dma_unencrypted(dev)) {
|
||||
if (set_memory_decrypted((unsigned long)ret,
|
||||
1 << get_order(size)))
|
||||
goto out_free_pages;
|
||||
}
|
||||
memset(ret, 0, size);
|
||||
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
|
||||
return page;
|
||||
out_free_pages:
|
||||
dma_free_contiguous(dev, page, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dma_direct_free(struct device *dev, size_t size,
|
||||
void *cpu_addr, dma_addr_t dma_addr, unsigned long attrs)
|
||||
void dma_direct_free_pages(struct device *dev, size_t size,
|
||||
struct page *page, dma_addr_t dma_addr,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_ARCH_HAS_DMA_SET_UNCACHED) &&
|
||||
!IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
|
||||
dma_alloc_need_uncached(dev, attrs))
|
||||
arch_dma_free(dev, size, cpu_addr, dma_addr, attrs);
|
||||
else
|
||||
dma_direct_free_pages(dev, size, cpu_addr, dma_addr, attrs);
|
||||
unsigned int page_order = get_order(size);
|
||||
void *vaddr = page_address(page);
|
||||
|
||||
/* If cpu_addr is not from an atomic pool, dma_free_from_pool() fails */
|
||||
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
|
||||
dma_free_from_pool(dev, vaddr, size))
|
||||
return;
|
||||
|
||||
if (force_dma_unencrypted(dev))
|
||||
set_memory_encrypted((unsigned long)vaddr, 1 << page_order);
|
||||
|
||||
dma_free_contiguous(dev, page, size);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
|
||||
@@ -345,6 +372,9 @@ void dma_direct_sync_sg_for_cpu(struct device *dev,
|
||||
if (unlikely(is_swiotlb_buffer(paddr)))
|
||||
swiotlb_tbl_sync_single(dev, paddr, sg->length, dir,
|
||||
SYNC_FOR_CPU);
|
||||
|
||||
if (dir == DMA_FROM_DEVICE)
|
||||
arch_dma_mark_clean(paddr, sg->length);
|
||||
}
|
||||
|
||||
if (!dev_is_dma_coherent(dev))
|
||||
@@ -453,13 +483,13 @@ int dma_direct_supported(struct device *dev, u64 mask)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* This check needs to be against the actual bit mask value, so
|
||||
* use __phys_to_dma() here so that the SME encryption mask isn't
|
||||
* This check needs to be against the actual bit mask value, so use
|
||||
* phys_to_dma_unencrypted() here so that the SME encryption mask isn't
|
||||
* part of the check.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA))
|
||||
min_mask = min_t(u64, min_mask, DMA_BIT_MASK(zone_dma_bits));
|
||||
return mask >= __phys_to_dma(dev, min_mask);
|
||||
return mask >= phys_to_dma_unencrypted(dev, min_mask);
|
||||
}
|
||||
|
||||
size_t dma_direct_max_mapping_size(struct device *dev)
|
||||
@@ -476,3 +506,45 @@ bool dma_direct_need_sync(struct device *dev, dma_addr_t dma_addr)
|
||||
return !dev_is_dma_coherent(dev) ||
|
||||
is_swiotlb_buffer(dma_to_phys(dev, dma_addr));
|
||||
}
|
||||
|
||||
/**
|
||||
* dma_direct_set_offset - Assign scalar offset for a single DMA range.
|
||||
* @dev: device pointer; needed to "own" the alloced memory.
|
||||
* @cpu_start: beginning of memory region covered by this offset.
|
||||
* @dma_start: beginning of DMA/PCI region covered by this offset.
|
||||
* @size: size of the region.
|
||||
*
|
||||
* This is for the simple case of a uniform offset which cannot
|
||||
* be discovered by "dma-ranges".
|
||||
*
|
||||
* It returns -ENOMEM if out of memory, -EINVAL if a map
|
||||
* already exists, 0 otherwise.
|
||||
*
|
||||
* Note: any call to this from a driver is a bug. The mapping needs
|
||||
* to be described by the device tree or other firmware interfaces.
|
||||
*/
|
||||
int dma_direct_set_offset(struct device *dev, phys_addr_t cpu_start,
|
||||
dma_addr_t dma_start, u64 size)
|
||||
{
|
||||
struct bus_dma_region *map;
|
||||
u64 offset = (u64)cpu_start - (u64)dma_start;
|
||||
|
||||
if (dev->dma_range_map) {
|
||||
dev_err(dev, "attempt to add DMA range to existing map\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!offset)
|
||||
return 0;
|
||||
|
||||
map = kcalloc(2, sizeof(*map), GFP_KERNEL);
|
||||
if (!map)
|
||||
return -ENOMEM;
|
||||
map[0].cpu_start = cpu_start;
|
||||
map[0].dma_start = dma_start;
|
||||
map[0].offset = offset;
|
||||
map[0].size = size;
|
||||
dev->dma_range_map = map;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_direct_set_offset);
|
||||
|
119
kernel/dma/direct.h
Normal file
119
kernel/dma/direct.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/* 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>
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
if (unlikely(is_swiotlb_buffer(paddr)))
|
||||
swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_DEVICE);
|
||||
|
||||
if (!dev_is_dma_coherent(dev))
|
||||
arch_sync_dma_for_device(paddr, size, dir);
|
||||
}
|
||||
|
||||
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_for_cpu_all();
|
||||
}
|
||||
|
||||
if (unlikely(is_swiotlb_buffer(paddr)))
|
||||
swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_CPU);
|
||||
|
||||
if (dir == DMA_FROM_DEVICE)
|
||||
arch_dma_mark_clean(paddr, size);
|
||||
}
|
||||
|
||||
static inline dma_addr_t dma_direct_map_page(struct device *dev,
|
||||
struct page *page, unsigned long offset, size_t size,
|
||||
enum dma_data_direction dir, unsigned long attrs)
|
||||
{
|
||||
phys_addr_t phys = page_to_phys(page) + offset;
|
||||
dma_addr_t dma_addr = phys_to_dma(dev, phys);
|
||||
|
||||
if (unlikely(swiotlb_force == SWIOTLB_FORCE))
|
||||
return swiotlb_map(dev, phys, size, dir, attrs);
|
||||
|
||||
if (unlikely(!dma_capable(dev, dma_addr, size, true))) {
|
||||
if (swiotlb_force != SWIOTLB_NO_FORCE)
|
||||
return swiotlb_map(dev, phys, size, dir, attrs);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
|
||||
arch_sync_dma_for_device(phys, size, dir);
|
||||
return dma_addr;
|
||||
}
|
||||
|
||||
static inline void dma_direct_unmap_page(struct device *dev, dma_addr_t addr,
|
||||
size_t size, enum dma_data_direction dir, unsigned long attrs)
|
||||
{
|
||||
phys_addr_t phys = dma_to_phys(dev, addr);
|
||||
|
||||
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
|
||||
dma_direct_sync_single_for_cpu(dev, addr, size, dir);
|
||||
|
||||
if (unlikely(is_swiotlb_buffer(phys)))
|
||||
swiotlb_tbl_unmap_single(dev, phys, size, size, dir, attrs);
|
||||
}
|
||||
#endif /* _KERNEL_DMA_DIRECT_H */
|
@@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Dummy DMA ops that always fail.
|
||||
*/
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
|
||||
static int dma_dummy_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
@@ -36,4 +36,3 @@ const struct dma_map_ops dma_dummy_ops = {
|
||||
.map_sg = dma_dummy_map_sg,
|
||||
.dma_supported = dma_dummy_supported,
|
||||
};
|
||||
EXPORT_SYMBOL(dma_dummy_ops);
|
||||
|
@@ -7,13 +7,14 @@
|
||||
*/
|
||||
#include <linux/memblock.h> /* for max_pfn */
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-noncoherent.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include "debug.h"
|
||||
#include "direct.h"
|
||||
|
||||
/*
|
||||
* Managed DMA API
|
||||
@@ -144,6 +145,10 @@ dma_addr_t dma_map_page_attrs(struct device *dev, struct page *page,
|
||||
dma_addr_t addr;
|
||||
|
||||
BUG_ON(!valid_dma_direction(dir));
|
||||
|
||||
if (WARN_ON_ONCE(!dev->dma_mask))
|
||||
return DMA_MAPPING_ERROR;
|
||||
|
||||
if (dma_map_direct(dev, ops))
|
||||
addr = dma_direct_map_page(dev, page, offset, size, dir, attrs);
|
||||
else
|
||||
@@ -179,6 +184,10 @@ int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, int nents,
|
||||
int ents;
|
||||
|
||||
BUG_ON(!valid_dma_direction(dir));
|
||||
|
||||
if (WARN_ON_ONCE(!dev->dma_mask))
|
||||
return 0;
|
||||
|
||||
if (dma_map_direct(dev, ops))
|
||||
ents = dma_direct_map_sg(dev, sg, nents, dir, attrs);
|
||||
else
|
||||
@@ -213,6 +222,9 @@ dma_addr_t dma_map_resource(struct device *dev, phys_addr_t phys_addr,
|
||||
|
||||
BUG_ON(!valid_dma_direction(dir));
|
||||
|
||||
if (WARN_ON_ONCE(!dev->dma_mask))
|
||||
return DMA_MAPPING_ERROR;
|
||||
|
||||
/* Don't allow RAM to be mapped */
|
||||
if (WARN_ON_ONCE(pfn_valid(PHYS_PFN(phys_addr))))
|
||||
return DMA_MAPPING_ERROR;
|
||||
@@ -295,22 +307,6 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
|
||||
}
|
||||
EXPORT_SYMBOL(dma_sync_sg_for_device);
|
||||
|
||||
/*
|
||||
* Create scatter-list for the already allocated DMA buffer.
|
||||
*/
|
||||
int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
unsigned long attrs)
|
||||
{
|
||||
struct page *page = virt_to_page(cpu_addr);
|
||||
int ret;
|
||||
|
||||
ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
|
||||
if (!ret)
|
||||
sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The whole dma_get_sgtable() idea is fundamentally unsafe - it seems
|
||||
* that the intention is to allow exporting memory allocated via the
|
||||
@@ -346,9 +342,7 @@ pgprot_t dma_pgprot(struct device *dev, pgprot_t prot, unsigned long attrs)
|
||||
{
|
||||
if (force_dma_unencrypted(dev))
|
||||
prot = pgprot_decrypted(prot);
|
||||
if (dev_is_dma_coherent(dev) ||
|
||||
(IS_ENABLED(CONFIG_DMA_NONCOHERENT_CACHE_SYNC) &&
|
||||
(attrs & DMA_ATTR_NON_CONSISTENT)))
|
||||
if (dev_is_dma_coherent(dev))
|
||||
return prot;
|
||||
#ifdef CONFIG_ARCH_HAS_DMA_WRITE_COMBINE
|
||||
if (attrs & DMA_ATTR_WRITE_COMBINE)
|
||||
@@ -358,35 +352,6 @@ pgprot_t dma_pgprot(struct device *dev, pgprot_t prot, unsigned long attrs)
|
||||
}
|
||||
#endif /* CONFIG_MMU */
|
||||
|
||||
/*
|
||||
* Create userspace mapping for the DMA-coherent memory.
|
||||
*/
|
||||
int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
unsigned long attrs)
|
||||
{
|
||||
#ifdef CONFIG_MMU
|
||||
unsigned long user_count = vma_pages(vma);
|
||||
unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
unsigned long off = vma->vm_pgoff;
|
||||
int ret = -ENXIO;
|
||||
|
||||
vma->vm_page_prot = dma_pgprot(dev, vma->vm_page_prot, attrs);
|
||||
|
||||
if (dma_mmap_from_dev_coherent(dev, vma, cpu_addr, size, &ret))
|
||||
return ret;
|
||||
|
||||
if (off >= count || user_count > count - off)
|
||||
return -ENXIO;
|
||||
|
||||
return remap_pfn_range(vma, vma->vm_start,
|
||||
page_to_pfn(virt_to_page(cpu_addr)) + vma->vm_pgoff,
|
||||
user_count << PAGE_SHIFT, vma->vm_page_prot);
|
||||
#else
|
||||
return -ENXIO;
|
||||
#endif /* CONFIG_MMU */
|
||||
}
|
||||
|
||||
/**
|
||||
* dma_can_mmap - check if a given device supports dma_mmap_*
|
||||
* @dev: device to check
|
||||
@@ -506,6 +471,86 @@ void dma_free_attrs(struct device *dev, size_t size, void *cpu_addr,
|
||||
}
|
||||
EXPORT_SYMBOL(dma_free_attrs);
|
||||
|
||||
struct page *dma_alloc_pages(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
struct page *page;
|
||||
|
||||
if (WARN_ON_ONCE(!dev->coherent_dma_mask))
|
||||
return NULL;
|
||||
if (WARN_ON_ONCE(gfp & (__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM)))
|
||||
return NULL;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
if (dma_alloc_direct(dev, ops))
|
||||
page = dma_direct_alloc_pages(dev, size, dma_handle, dir, gfp);
|
||||
else if (ops->alloc_pages)
|
||||
page = ops->alloc_pages(dev, size, dma_handle, dir, gfp);
|
||||
else
|
||||
return NULL;
|
||||
|
||||
debug_dma_map_page(dev, page, 0, size, dir, *dma_handle);
|
||||
|
||||
return page;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_alloc_pages);
|
||||
|
||||
void dma_free_pages(struct device *dev, size_t size, struct page *page,
|
||||
dma_addr_t dma_handle, enum dma_data_direction dir)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
debug_dma_unmap_page(dev, dma_handle, size, dir);
|
||||
|
||||
if (dma_alloc_direct(dev, ops))
|
||||
dma_direct_free_pages(dev, size, page, dma_handle, dir);
|
||||
else if (ops->free_pages)
|
||||
ops->free_pages(dev, size, page, dma_handle, dir);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_free_pages);
|
||||
|
||||
void *dma_alloc_noncoherent(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
void *vaddr;
|
||||
|
||||
if (!ops || !ops->alloc_noncoherent) {
|
||||
struct page *page;
|
||||
|
||||
page = dma_alloc_pages(dev, size, dma_handle, dir, gfp);
|
||||
if (!page)
|
||||
return NULL;
|
||||
return page_address(page);
|
||||
}
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
vaddr = ops->alloc_noncoherent(dev, size, dma_handle, dir, gfp);
|
||||
if (vaddr)
|
||||
debug_dma_map_page(dev, virt_to_page(vaddr), 0, size, dir,
|
||||
*dma_handle);
|
||||
return vaddr;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_alloc_noncoherent);
|
||||
|
||||
void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr,
|
||||
dma_addr_t dma_handle, enum dma_data_direction dir)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
if (!ops || !ops->free_noncoherent) {
|
||||
dma_free_pages(dev, size, virt_to_page(vaddr), dma_handle, dir);
|
||||
return;
|
||||
}
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
debug_dma_unmap_page(dev, dma_handle, size, dir);
|
||||
ops->free_noncoherent(dev, size, vaddr, dma_handle, dir);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_free_noncoherent);
|
||||
|
||||
int dma_supported(struct device *dev, u64 mask)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
@@ -563,20 +608,6 @@ int dma_set_coherent_mask(struct device *dev, u64 mask)
|
||||
EXPORT_SYMBOL(dma_set_coherent_mask);
|
||||
#endif
|
||||
|
||||
void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
BUG_ON(!valid_dma_direction(dir));
|
||||
|
||||
if (dma_alloc_direct(dev, ops))
|
||||
arch_dma_cache_sync(dev, vaddr, size, dir);
|
||||
else if (ops->cache_sync)
|
||||
ops->cache_sync(dev, vaddr, size, dir);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_cache_sync);
|
||||
|
||||
size_t dma_max_mapping_size(struct device *dev)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
85
kernel/dma/ops_helpers.c
Normal file
85
kernel/dma/ops_helpers.c
Normal file
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Helpers for DMA ops implementations. These generally rely on the fact that
|
||||
* the allocated memory contains normal pages in the direct kernel mapping.
|
||||
*/
|
||||
#include <linux/dma-map-ops.h>
|
||||
|
||||
/*
|
||||
* Create scatter-list for the already allocated DMA buffer.
|
||||
*/
|
||||
int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
unsigned long attrs)
|
||||
{
|
||||
struct page *page = virt_to_page(cpu_addr);
|
||||
int ret;
|
||||
|
||||
ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
|
||||
if (!ret)
|
||||
sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create userspace mapping for the DMA-coherent memory.
|
||||
*/
|
||||
int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
unsigned long attrs)
|
||||
{
|
||||
#ifdef CONFIG_MMU
|
||||
unsigned long user_count = vma_pages(vma);
|
||||
unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
unsigned long off = vma->vm_pgoff;
|
||||
int ret = -ENXIO;
|
||||
|
||||
vma->vm_page_prot = dma_pgprot(dev, vma->vm_page_prot, attrs);
|
||||
|
||||
if (dma_mmap_from_dev_coherent(dev, vma, cpu_addr, size, &ret))
|
||||
return ret;
|
||||
|
||||
if (off >= count || user_count > count - off)
|
||||
return -ENXIO;
|
||||
|
||||
return remap_pfn_range(vma, vma->vm_start,
|
||||
page_to_pfn(virt_to_page(cpu_addr)) + vma->vm_pgoff,
|
||||
user_count << PAGE_SHIFT, vma->vm_page_prot);
|
||||
#else
|
||||
return -ENXIO;
|
||||
#endif /* CONFIG_MMU */
|
||||
}
|
||||
|
||||
struct page *dma_common_alloc_pages(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
struct page *page;
|
||||
|
||||
page = dma_alloc_contiguous(dev, size, gfp);
|
||||
if (!page)
|
||||
page = alloc_pages_node(dev_to_node(dev), gfp, get_order(size));
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
*dma_handle = ops->map_page(dev, page, 0, size, dir,
|
||||
DMA_ATTR_SKIP_CPU_SYNC);
|
||||
if (*dma_handle == DMA_MAPPING_ERROR) {
|
||||
dma_free_contiguous(dev, page, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(page_address(page), 0, size);
|
||||
return page;
|
||||
}
|
||||
|
||||
void dma_common_free_pages(struct device *dev, size_t size, struct page *page,
|
||||
dma_addr_t dma_handle, enum dma_data_direction dir)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
if (ops->unmap_page)
|
||||
ops->unmap_page(dev, dma_handle, size, dir,
|
||||
DMA_ATTR_SKIP_CPU_SYNC);
|
||||
dma_free_contiguous(dev, page, size);
|
||||
}
|
@@ -5,9 +5,8 @@
|
||||
*/
|
||||
#include <linux/cma.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-noncoherent.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/set_memory.h>
|
||||
@@ -115,7 +114,7 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
|
||||
#endif
|
||||
/*
|
||||
* Memory in the atomic DMA pools must be unencrypted, the pools do not
|
||||
* shrink so no re-encryption occurs in dma_direct_free_pages().
|
||||
* shrink so no re-encryption occurs in dma_direct_free().
|
||||
*/
|
||||
ret = set_memory_decrypted((unsigned long)page_to_virt(page),
|
||||
1 << order);
|
||||
|
@@ -22,7 +22,7 @@
|
||||
|
||||
#include <linux/cache.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-noncoherent.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/spinlock.h>
|
||||
@@ -668,13 +668,13 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size,
|
||||
swiotlb_force);
|
||||
|
||||
swiotlb_addr = swiotlb_tbl_map_single(dev,
|
||||
__phys_to_dma(dev, io_tlb_start),
|
||||
phys_to_dma_unencrypted(dev, io_tlb_start),
|
||||
paddr, size, size, dir, attrs);
|
||||
if (swiotlb_addr == (phys_addr_t)DMA_MAPPING_ERROR)
|
||||
return DMA_MAPPING_ERROR;
|
||||
|
||||
/* Ensure that the address returned is DMA'ble */
|
||||
dma_addr = __phys_to_dma(dev, swiotlb_addr);
|
||||
dma_addr = phys_to_dma_unencrypted(dev, swiotlb_addr);
|
||||
if (unlikely(!dma_capable(dev, dma_addr, size, true))) {
|
||||
swiotlb_tbl_unmap_single(dev, swiotlb_addr, size, size, dir,
|
||||
attrs | DMA_ATTR_SKIP_CPU_SYNC);
|
||||
|
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
#include <linux/export.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
static void *dma_virt_alloc(struct device *dev, size_t size,
|
||||
@@ -55,5 +55,7 @@ const struct dma_map_ops dma_virt_ops = {
|
||||
.free = dma_virt_free,
|
||||
.map_page = dma_virt_map_page,
|
||||
.map_sg = dma_virt_map_sg,
|
||||
.alloc_pages = dma_common_alloc_pages,
|
||||
.free_pages = dma_common_free_pages,
|
||||
};
|
||||
EXPORT_SYMBOL(dma_virt_ops);
|
||||
|
Reference in New Issue
Block a user