Merge tag 'dma-mapping-4.21' of git://git.infradead.org/users/hch/dma-mapping
Pull DMA mapping updates from Christoph Hellwig: "A huge update this time, but a lot of that is just consolidating or removing code: - provide a common DMA_MAPPING_ERROR definition and avoid indirect calls for dma_map_* error checking - use direct calls for the DMA direct mapping case, avoiding huge retpoline overhead for high performance workloads - merge the swiotlb dma_map_ops into dma-direct - provide a generic remapping DMA consistent allocator for architectures that have devices that perform DMA that is not cache coherent. Based on the existing arm64 implementation and also used for csky now. - improve the dma-debug infrastructure, including dynamic allocation of entries (Robin Murphy) - default to providing chaining scatterlist everywhere, with opt-outs for the few architectures (alpha, parisc, most arm32 variants) that can't cope with it - misc sparc32 dma-related cleanups - remove the dma_mark_clean arch hook used by swiotlb on ia64 and replace it with the generic noncoherent infrastructure - fix the return type of dma_set_max_seg_size (Niklas Söderlund) - move the dummy dma ops for not DMA capable devices from arm64 to common code (Robin Murphy) - ensure dma_alloc_coherent returns zeroed memory to avoid kernel data leaks through userspace. We already did this for most common architectures, but this ensures we do it everywhere. dma_zalloc_coherent has been deprecated and can hopefully be removed after -rc1 with a coccinelle script" * tag 'dma-mapping-4.21' of git://git.infradead.org/users/hch/dma-mapping: (73 commits) dma-mapping: fix inverted logic in dma_supported dma-mapping: deprecate dma_zalloc_coherent dma-mapping: zero memory returned from dma_alloc_* sparc/iommu: fix ->map_sg return value sparc/io-unit: fix ->map_sg return value arm64: default to the direct mapping in get_arch_dma_ops PCI: Remove unused attr variable in pci_dma_configure ia64: only select ARCH_HAS_DMA_COHERENT_TO_PFN if swiotlb is enabled dma-mapping: bypass indirect calls for dma-direct vmd: use the proper dma_* APIs instead of direct methods calls dma-direct: merge swiotlb_dma_ops into the dma_direct code dma-direct: use dma_direct_map_page to implement dma_direct_map_sg dma-direct: improve addressability error reporting swiotlb: remove dma_mark_clean swiotlb: remove SWIOTLB_MAP_ERROR ACPI / scan: Refactor _CCA enforcement dma-mapping: factor out dummy DMA ops dma-mapping: always build the direct mapping code dma-mapping: move dma_cache_sync out of line dma-mapping: move various slow path functions out of line ...
This commit is contained in:
@@ -35,13 +35,8 @@ config ARCH_HAS_DMA_COHERENT_TO_PFN
|
||||
config ARCH_HAS_DMA_MMAP_PGPROT
|
||||
bool
|
||||
|
||||
config DMA_DIRECT_OPS
|
||||
bool
|
||||
depends on HAS_DMA
|
||||
|
||||
config DMA_NONCOHERENT_CACHE_SYNC
|
||||
bool
|
||||
depends on DMA_DIRECT_OPS
|
||||
|
||||
config DMA_VIRT_OPS
|
||||
bool
|
||||
@@ -49,5 +44,12 @@ config DMA_VIRT_OPS
|
||||
|
||||
config SWIOTLB
|
||||
bool
|
||||
select DMA_DIRECT_OPS
|
||||
select NEED_DMA_MAP_STATE
|
||||
|
||||
config DMA_REMAP
|
||||
depends on MMU
|
||||
bool
|
||||
|
||||
config DMA_DIRECT_REMAP
|
||||
bool
|
||||
select DMA_REMAP
|
||||
|
@@ -1,10 +1,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_HAS_DMA) += mapping.o
|
||||
obj-$(CONFIG_HAS_DMA) += mapping.o direct.o dummy.o
|
||||
obj-$(CONFIG_DMA_CMA) += contiguous.o
|
||||
obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += coherent.o
|
||||
obj-$(CONFIG_DMA_DIRECT_OPS) += direct.o
|
||||
obj-$(CONFIG_DMA_VIRT_OPS) += virt.o
|
||||
obj-$(CONFIG_DMA_API_DEBUG) += debug.o
|
||||
obj-$(CONFIG_SWIOTLB) += swiotlb.o
|
||||
|
||||
obj-$(CONFIG_DMA_REMAP) += remap.o
|
||||
|
@@ -17,6 +17,8 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "DMA-API: " fmt
|
||||
|
||||
#include <linux/sched/task_stack.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
@@ -41,10 +43,9 @@
|
||||
#define HASH_FN_SHIFT 13
|
||||
#define HASH_FN_MASK (HASH_SIZE - 1)
|
||||
|
||||
/* allow architectures to override this if absolutely required */
|
||||
#ifndef PREALLOC_DMA_DEBUG_ENTRIES
|
||||
#define PREALLOC_DMA_DEBUG_ENTRIES (1 << 16)
|
||||
#endif
|
||||
/* If the pool runs out, add this many new entries at once */
|
||||
#define DMA_DEBUG_DYNAMIC_ENTRIES (PAGE_SIZE / sizeof(struct dma_debug_entry))
|
||||
|
||||
enum {
|
||||
dma_debug_single,
|
||||
@@ -142,6 +143,7 @@ static struct dentry *show_all_errors_dent __read_mostly;
|
||||
static struct dentry *show_num_errors_dent __read_mostly;
|
||||
static struct dentry *num_free_entries_dent __read_mostly;
|
||||
static struct dentry *min_free_entries_dent __read_mostly;
|
||||
static struct dentry *nr_total_entries_dent __read_mostly;
|
||||
static struct dentry *filter_dent __read_mostly;
|
||||
|
||||
/* per-driver filter related state */
|
||||
@@ -234,7 +236,7 @@ static bool driver_filter(struct device *dev)
|
||||
error_count += 1; \
|
||||
if (driver_filter(dev) && \
|
||||
(show_all_errors || show_num_errors > 0)) { \
|
||||
WARN(1, "%s %s: " format, \
|
||||
WARN(1, pr_fmt("%s %s: ") format, \
|
||||
dev ? dev_driver_string(dev) : "NULL", \
|
||||
dev ? dev_name(dev) : "NULL", ## arg); \
|
||||
dump_entry_trace(entry); \
|
||||
@@ -519,7 +521,7 @@ static void active_cacheline_inc_overlap(phys_addr_t cln)
|
||||
* prematurely.
|
||||
*/
|
||||
WARN_ONCE(overlap > ACTIVE_CACHELINE_MAX_OVERLAP,
|
||||
"DMA-API: exceeded %d overlapping mappings of cacheline %pa\n",
|
||||
pr_fmt("exceeded %d overlapping mappings of cacheline %pa\n"),
|
||||
ACTIVE_CACHELINE_MAX_OVERLAP, &cln);
|
||||
}
|
||||
|
||||
@@ -614,7 +616,7 @@ void debug_dma_assert_idle(struct page *page)
|
||||
|
||||
cln = to_cacheline_number(entry);
|
||||
err_printk(entry->dev, entry,
|
||||
"DMA-API: cpu touching an active dma mapped cacheline [cln=%pa]\n",
|
||||
"cpu touching an active dma mapped cacheline [cln=%pa]\n",
|
||||
&cln);
|
||||
}
|
||||
|
||||
@@ -634,7 +636,7 @@ static void add_dma_entry(struct dma_debug_entry *entry)
|
||||
|
||||
rc = active_cacheline_insert(entry);
|
||||
if (rc == -ENOMEM) {
|
||||
pr_err("DMA-API: cacheline tracking ENOMEM, dma-debug disabled\n");
|
||||
pr_err("cacheline tracking ENOMEM, dma-debug disabled\n");
|
||||
global_disable = true;
|
||||
}
|
||||
|
||||
@@ -643,6 +645,24 @@ static void add_dma_entry(struct dma_debug_entry *entry)
|
||||
*/
|
||||
}
|
||||
|
||||
static int dma_debug_create_entries(gfp_t gfp)
|
||||
{
|
||||
struct dma_debug_entry *entry;
|
||||
int i;
|
||||
|
||||
entry = (void *)get_zeroed_page(gfp);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < DMA_DEBUG_DYNAMIC_ENTRIES; i++)
|
||||
list_add_tail(&entry[i].list, &free_entries);
|
||||
|
||||
num_free_entries += DMA_DEBUG_DYNAMIC_ENTRIES;
|
||||
nr_total_entries += DMA_DEBUG_DYNAMIC_ENTRIES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dma_debug_entry *__dma_entry_alloc(void)
|
||||
{
|
||||
struct dma_debug_entry *entry;
|
||||
@@ -658,6 +678,18 @@ static struct dma_debug_entry *__dma_entry_alloc(void)
|
||||
return entry;
|
||||
}
|
||||
|
||||
void __dma_entry_alloc_check_leak(void)
|
||||
{
|
||||
u32 tmp = nr_total_entries % nr_prealloc_entries;
|
||||
|
||||
/* Shout each time we tick over some multiple of the initial pool */
|
||||
if (tmp < DMA_DEBUG_DYNAMIC_ENTRIES) {
|
||||
pr_info("dma_debug_entry pool grown to %u (%u00%%)\n",
|
||||
nr_total_entries,
|
||||
(nr_total_entries / nr_prealloc_entries));
|
||||
}
|
||||
}
|
||||
|
||||
/* struct dma_entry allocator
|
||||
*
|
||||
* The next two functions implement the allocator for
|
||||
@@ -669,12 +701,14 @@ static struct dma_debug_entry *dma_entry_alloc(void)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&free_entries_lock, flags);
|
||||
|
||||
if (list_empty(&free_entries)) {
|
||||
global_disable = true;
|
||||
spin_unlock_irqrestore(&free_entries_lock, flags);
|
||||
pr_err("DMA-API: debugging out of memory - disabling\n");
|
||||
return NULL;
|
||||
if (num_free_entries == 0) {
|
||||
if (dma_debug_create_entries(GFP_ATOMIC)) {
|
||||
global_disable = true;
|
||||
spin_unlock_irqrestore(&free_entries_lock, flags);
|
||||
pr_err("debugging out of memory - disabling\n");
|
||||
return NULL;
|
||||
}
|
||||
__dma_entry_alloc_check_leak();
|
||||
}
|
||||
|
||||
entry = __dma_entry_alloc();
|
||||
@@ -707,52 +741,6 @@ static void dma_entry_free(struct dma_debug_entry *entry)
|
||||
spin_unlock_irqrestore(&free_entries_lock, flags);
|
||||
}
|
||||
|
||||
int dma_debug_resize_entries(u32 num_entries)
|
||||
{
|
||||
int i, delta, ret = 0;
|
||||
unsigned long flags;
|
||||
struct dma_debug_entry *entry;
|
||||
LIST_HEAD(tmp);
|
||||
|
||||
spin_lock_irqsave(&free_entries_lock, flags);
|
||||
|
||||
if (nr_total_entries < num_entries) {
|
||||
delta = num_entries - nr_total_entries;
|
||||
|
||||
spin_unlock_irqrestore(&free_entries_lock, flags);
|
||||
|
||||
for (i = 0; i < delta; i++) {
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
break;
|
||||
|
||||
list_add_tail(&entry->list, &tmp);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&free_entries_lock, flags);
|
||||
|
||||
list_splice(&tmp, &free_entries);
|
||||
nr_total_entries += i;
|
||||
num_free_entries += i;
|
||||
} else {
|
||||
delta = nr_total_entries - num_entries;
|
||||
|
||||
for (i = 0; i < delta && !list_empty(&free_entries); i++) {
|
||||
entry = __dma_entry_alloc();
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
nr_total_entries -= i;
|
||||
}
|
||||
|
||||
if (nr_total_entries != num_entries)
|
||||
ret = 1;
|
||||
|
||||
spin_unlock_irqrestore(&free_entries_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* DMA-API debugging init code
|
||||
*
|
||||
@@ -761,36 +749,6 @@ int dma_debug_resize_entries(u32 num_entries)
|
||||
* 2. Preallocate a given number of dma_debug_entry structs
|
||||
*/
|
||||
|
||||
static int prealloc_memory(u32 num_entries)
|
||||
{
|
||||
struct dma_debug_entry *entry, *next_entry;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_entries; ++i) {
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
goto out_err;
|
||||
|
||||
list_add_tail(&entry->list, &free_entries);
|
||||
}
|
||||
|
||||
num_free_entries = num_entries;
|
||||
min_free_entries = num_entries;
|
||||
|
||||
pr_info("DMA-API: preallocated %d debug entries\n", num_entries);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
|
||||
list_for_each_entry_safe(entry, next_entry, &free_entries, list) {
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static ssize_t filter_read(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
@@ -850,7 +808,7 @@ static ssize_t filter_write(struct file *file, const char __user *userbuf,
|
||||
* switched off.
|
||||
*/
|
||||
if (current_driver_name[0])
|
||||
pr_info("DMA-API: switching off dma-debug driver filter\n");
|
||||
pr_info("switching off dma-debug driver filter\n");
|
||||
current_driver_name[0] = 0;
|
||||
current_driver = NULL;
|
||||
goto out_unlock;
|
||||
@@ -868,7 +826,7 @@ static ssize_t filter_write(struct file *file, const char __user *userbuf,
|
||||
current_driver_name[i] = 0;
|
||||
current_driver = NULL;
|
||||
|
||||
pr_info("DMA-API: enable driver filter for driver [%s]\n",
|
||||
pr_info("enable driver filter for driver [%s]\n",
|
||||
current_driver_name);
|
||||
|
||||
out_unlock:
|
||||
@@ -887,7 +845,7 @@ static int dma_debug_fs_init(void)
|
||||
{
|
||||
dma_debug_dent = debugfs_create_dir("dma-api", NULL);
|
||||
if (!dma_debug_dent) {
|
||||
pr_err("DMA-API: can not create debugfs directory\n");
|
||||
pr_err("can not create debugfs directory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -926,6 +884,12 @@ static int dma_debug_fs_init(void)
|
||||
if (!min_free_entries_dent)
|
||||
goto out_err;
|
||||
|
||||
nr_total_entries_dent = debugfs_create_u32("nr_total_entries", 0444,
|
||||
dma_debug_dent,
|
||||
&nr_total_entries);
|
||||
if (!nr_total_entries_dent)
|
||||
goto out_err;
|
||||
|
||||
filter_dent = debugfs_create_file("driver_filter", 0644,
|
||||
dma_debug_dent, NULL, &filter_fops);
|
||||
if (!filter_dent)
|
||||
@@ -973,7 +937,7 @@ static int dma_debug_device_change(struct notifier_block *nb, unsigned long acti
|
||||
count = device_dma_allocations(dev, &entry);
|
||||
if (count == 0)
|
||||
break;
|
||||
err_printk(dev, entry, "DMA-API: device driver has pending "
|
||||
err_printk(dev, entry, "device driver has pending "
|
||||
"DMA allocations while released from device "
|
||||
"[count=%d]\n"
|
||||
"One of leaked entries details: "
|
||||
@@ -1009,7 +973,7 @@ void dma_debug_add_bus(struct bus_type *bus)
|
||||
|
||||
static int dma_debug_init(void)
|
||||
{
|
||||
int i;
|
||||
int i, nr_pages;
|
||||
|
||||
/* Do not use dma_debug_initialized here, since we really want to be
|
||||
* called to set dma_debug_initialized
|
||||
@@ -1023,24 +987,31 @@ static int dma_debug_init(void)
|
||||
}
|
||||
|
||||
if (dma_debug_fs_init() != 0) {
|
||||
pr_err("DMA-API: error creating debugfs entries - disabling\n");
|
||||
pr_err("error creating debugfs entries - disabling\n");
|
||||
global_disable = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (prealloc_memory(nr_prealloc_entries) != 0) {
|
||||
pr_err("DMA-API: debugging out of memory error - disabled\n");
|
||||
nr_pages = DIV_ROUND_UP(nr_prealloc_entries, DMA_DEBUG_DYNAMIC_ENTRIES);
|
||||
for (i = 0; i < nr_pages; ++i)
|
||||
dma_debug_create_entries(GFP_KERNEL);
|
||||
if (num_free_entries >= nr_prealloc_entries) {
|
||||
pr_info("preallocated %d debug entries\n", nr_total_entries);
|
||||
} else if (num_free_entries > 0) {
|
||||
pr_warn("%d debug entries requested but only %d allocated\n",
|
||||
nr_prealloc_entries, nr_total_entries);
|
||||
} else {
|
||||
pr_err("debugging out of memory error - disabled\n");
|
||||
global_disable = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
nr_total_entries = num_free_entries;
|
||||
min_free_entries = num_free_entries;
|
||||
|
||||
dma_debug_initialized = true;
|
||||
|
||||
pr_info("DMA-API: debugging enabled by kernel config\n");
|
||||
pr_info("debugging enabled by kernel config\n");
|
||||
return 0;
|
||||
}
|
||||
core_initcall(dma_debug_init);
|
||||
@@ -1051,7 +1022,7 @@ static __init int dma_debug_cmdline(char *str)
|
||||
return -EINVAL;
|
||||
|
||||
if (strncmp(str, "off", 3) == 0) {
|
||||
pr_info("DMA-API: debugging disabled on kernel command line\n");
|
||||
pr_info("debugging disabled on kernel command line\n");
|
||||
global_disable = true;
|
||||
}
|
||||
|
||||
@@ -1085,11 +1056,11 @@ static void check_unmap(struct dma_debug_entry *ref)
|
||||
|
||||
if (dma_mapping_error(ref->dev, ref->dev_addr)) {
|
||||
err_printk(ref->dev, NULL,
|
||||
"DMA-API: device driver tries to free an "
|
||||
"device driver tries to free an "
|
||||
"invalid DMA memory address\n");
|
||||
} else {
|
||||
err_printk(ref->dev, NULL,
|
||||
"DMA-API: device driver tries to free DMA "
|
||||
"device driver tries to free DMA "
|
||||
"memory it has not allocated [device "
|
||||
"address=0x%016llx] [size=%llu bytes]\n",
|
||||
ref->dev_addr, ref->size);
|
||||
@@ -1098,7 +1069,7 @@ static void check_unmap(struct dma_debug_entry *ref)
|
||||
}
|
||||
|
||||
if (ref->size != entry->size) {
|
||||
err_printk(ref->dev, entry, "DMA-API: device driver frees "
|
||||
err_printk(ref->dev, entry, "device driver frees "
|
||||
"DMA memory with different size "
|
||||
"[device address=0x%016llx] [map size=%llu bytes] "
|
||||
"[unmap size=%llu bytes]\n",
|
||||
@@ -1106,7 +1077,7 @@ static void check_unmap(struct dma_debug_entry *ref)
|
||||
}
|
||||
|
||||
if (ref->type != entry->type) {
|
||||
err_printk(ref->dev, entry, "DMA-API: device driver frees "
|
||||
err_printk(ref->dev, entry, "device driver frees "
|
||||
"DMA memory with wrong function "
|
||||
"[device address=0x%016llx] [size=%llu bytes] "
|
||||
"[mapped as %s] [unmapped as %s]\n",
|
||||
@@ -1114,7 +1085,7 @@ static void check_unmap(struct dma_debug_entry *ref)
|
||||
type2name[entry->type], type2name[ref->type]);
|
||||
} else if ((entry->type == dma_debug_coherent) &&
|
||||
(phys_addr(ref) != phys_addr(entry))) {
|
||||
err_printk(ref->dev, entry, "DMA-API: device driver frees "
|
||||
err_printk(ref->dev, entry, "device driver frees "
|
||||
"DMA memory with different CPU address "
|
||||
"[device address=0x%016llx] [size=%llu bytes] "
|
||||
"[cpu alloc address=0x%016llx] "
|
||||
@@ -1126,7 +1097,7 @@ static void check_unmap(struct dma_debug_entry *ref)
|
||||
|
||||
if (ref->sg_call_ents && ref->type == dma_debug_sg &&
|
||||
ref->sg_call_ents != entry->sg_call_ents) {
|
||||
err_printk(ref->dev, entry, "DMA-API: device driver frees "
|
||||
err_printk(ref->dev, entry, "device driver frees "
|
||||
"DMA sg list with different entry count "
|
||||
"[map count=%d] [unmap count=%d]\n",
|
||||
entry->sg_call_ents, ref->sg_call_ents);
|
||||
@@ -1137,7 +1108,7 @@ static void check_unmap(struct dma_debug_entry *ref)
|
||||
* DMA API don't handle this properly, so check for it here
|
||||
*/
|
||||
if (ref->direction != entry->direction) {
|
||||
err_printk(ref->dev, entry, "DMA-API: device driver frees "
|
||||
err_printk(ref->dev, entry, "device driver frees "
|
||||
"DMA memory with different direction "
|
||||
"[device address=0x%016llx] [size=%llu bytes] "
|
||||
"[mapped with %s] [unmapped with %s]\n",
|
||||
@@ -1153,7 +1124,7 @@ static void check_unmap(struct dma_debug_entry *ref)
|
||||
*/
|
||||
if (entry->map_err_type == MAP_ERR_NOT_CHECKED) {
|
||||
err_printk(ref->dev, entry,
|
||||
"DMA-API: device driver failed to check map error"
|
||||
"device driver failed to check map error"
|
||||
"[device address=0x%016llx] [size=%llu bytes] "
|
||||
"[mapped as %s]",
|
||||
ref->dev_addr, ref->size,
|
||||
@@ -1178,7 +1149,7 @@ static void check_for_stack(struct device *dev,
|
||||
return;
|
||||
addr = page_address(page) + offset;
|
||||
if (object_is_on_stack(addr))
|
||||
err_printk(dev, NULL, "DMA-API: device driver maps memory from stack [addr=%p]\n", addr);
|
||||
err_printk(dev, NULL, "device driver maps memory from stack [addr=%p]\n", addr);
|
||||
} else {
|
||||
/* Stack is vmalloced. */
|
||||
int i;
|
||||
@@ -1188,7 +1159,7 @@ static void check_for_stack(struct device *dev,
|
||||
continue;
|
||||
|
||||
addr = (u8 *)current->stack + i * PAGE_SIZE + offset;
|
||||
err_printk(dev, NULL, "DMA-API: device driver maps memory from stack [probable addr=%p]\n", addr);
|
||||
err_printk(dev, NULL, "device driver maps memory from stack [probable addr=%p]\n", addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1208,7 +1179,7 @@ static void check_for_illegal_area(struct device *dev, void *addr, unsigned long
|
||||
{
|
||||
if (overlap(addr, len, _stext, _etext) ||
|
||||
overlap(addr, len, __start_rodata, __end_rodata))
|
||||
err_printk(dev, NULL, "DMA-API: device driver maps memory from kernel text or rodata [addr=%p] [len=%lu]\n", addr, len);
|
||||
err_printk(dev, NULL, "device driver maps memory from kernel text or rodata [addr=%p] [len=%lu]\n", addr, len);
|
||||
}
|
||||
|
||||
static void check_sync(struct device *dev,
|
||||
@@ -1224,7 +1195,7 @@ static void check_sync(struct device *dev,
|
||||
entry = bucket_find_contain(&bucket, ref, &flags);
|
||||
|
||||
if (!entry) {
|
||||
err_printk(dev, NULL, "DMA-API: device driver tries "
|
||||
err_printk(dev, NULL, "device driver tries "
|
||||
"to sync DMA memory it has not allocated "
|
||||
"[device address=0x%016llx] [size=%llu bytes]\n",
|
||||
(unsigned long long)ref->dev_addr, ref->size);
|
||||
@@ -1232,7 +1203,7 @@ static void check_sync(struct device *dev,
|
||||
}
|
||||
|
||||
if (ref->size > entry->size) {
|
||||
err_printk(dev, entry, "DMA-API: device driver syncs"
|
||||
err_printk(dev, entry, "device driver syncs"
|
||||
" DMA memory outside allocated range "
|
||||
"[device address=0x%016llx] "
|
||||
"[allocation size=%llu bytes] "
|
||||
@@ -1245,7 +1216,7 @@ static void check_sync(struct device *dev,
|
||||
goto out;
|
||||
|
||||
if (ref->direction != entry->direction) {
|
||||
err_printk(dev, entry, "DMA-API: device driver syncs "
|
||||
err_printk(dev, entry, "device driver syncs "
|
||||
"DMA memory with different direction "
|
||||
"[device address=0x%016llx] [size=%llu bytes] "
|
||||
"[mapped with %s] [synced with %s]\n",
|
||||
@@ -1256,7 +1227,7 @@ static void check_sync(struct device *dev,
|
||||
|
||||
if (to_cpu && !(entry->direction == DMA_FROM_DEVICE) &&
|
||||
!(ref->direction == DMA_TO_DEVICE))
|
||||
err_printk(dev, entry, "DMA-API: device driver syncs "
|
||||
err_printk(dev, entry, "device driver syncs "
|
||||
"device read-only DMA memory for cpu "
|
||||
"[device address=0x%016llx] [size=%llu bytes] "
|
||||
"[mapped with %s] [synced with %s]\n",
|
||||
@@ -1266,7 +1237,7 @@ static void check_sync(struct device *dev,
|
||||
|
||||
if (!to_cpu && !(entry->direction == DMA_TO_DEVICE) &&
|
||||
!(ref->direction == DMA_FROM_DEVICE))
|
||||
err_printk(dev, entry, "DMA-API: device driver syncs "
|
||||
err_printk(dev, entry, "device driver syncs "
|
||||
"device write-only DMA memory to device "
|
||||
"[device address=0x%016llx] [size=%llu bytes] "
|
||||
"[mapped with %s] [synced with %s]\n",
|
||||
@@ -1276,7 +1247,7 @@ static void check_sync(struct device *dev,
|
||||
|
||||
if (ref->sg_call_ents && ref->type == dma_debug_sg &&
|
||||
ref->sg_call_ents != entry->sg_call_ents) {
|
||||
err_printk(ref->dev, entry, "DMA-API: device driver syncs "
|
||||
err_printk(ref->dev, entry, "device driver syncs "
|
||||
"DMA sg list with different entry count "
|
||||
"[map count=%d] [sync count=%d]\n",
|
||||
entry->sg_call_ents, ref->sg_call_ents);
|
||||
@@ -1297,7 +1268,7 @@ static void check_sg_segment(struct device *dev, struct scatterlist *sg)
|
||||
* whoever generated the list forgot to check them.
|
||||
*/
|
||||
if (sg->length > max_seg)
|
||||
err_printk(dev, NULL, "DMA-API: mapping sg segment longer than device claims to support [len=%u] [max=%u]\n",
|
||||
err_printk(dev, NULL, "mapping sg segment longer than device claims to support [len=%u] [max=%u]\n",
|
||||
sg->length, max_seg);
|
||||
/*
|
||||
* In some cases this could potentially be the DMA API
|
||||
@@ -1307,7 +1278,7 @@ static void check_sg_segment(struct device *dev, struct scatterlist *sg)
|
||||
start = sg_dma_address(sg);
|
||||
end = start + sg_dma_len(sg) - 1;
|
||||
if ((start ^ end) & ~boundary)
|
||||
err_printk(dev, NULL, "DMA-API: mapping sg segment across boundary [start=0x%016llx] [end=0x%016llx] [boundary=0x%016llx]\n",
|
||||
err_printk(dev, NULL, "mapping sg segment across boundary [start=0x%016llx] [end=0x%016llx] [boundary=0x%016llx]\n",
|
||||
start, end, boundary);
|
||||
#endif
|
||||
}
|
||||
@@ -1319,11 +1290,11 @@ void debug_dma_map_single(struct device *dev, const void *addr,
|
||||
return;
|
||||
|
||||
if (!virt_addr_valid(addr))
|
||||
err_printk(dev, NULL, "DMA-API: device driver maps memory from invalid area [addr=%p] [len=%lu]\n",
|
||||
err_printk(dev, NULL, "device driver maps memory from invalid area [addr=%p] [len=%lu]\n",
|
||||
addr, len);
|
||||
|
||||
if (is_vmalloc_addr(addr))
|
||||
err_printk(dev, NULL, "DMA-API: device driver maps memory from vmalloc area [addr=%p] [len=%lu]\n",
|
||||
err_printk(dev, NULL, "device driver maps memory from vmalloc area [addr=%p] [len=%lu]\n",
|
||||
addr, len);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_map_single);
|
||||
@@ -1662,48 +1633,6 @@ void debug_dma_sync_single_for_device(struct device *dev,
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_sync_single_for_device);
|
||||
|
||||
void debug_dma_sync_single_range_for_cpu(struct device *dev,
|
||||
dma_addr_t dma_handle,
|
||||
unsigned long offset, size_t size,
|
||||
int direction)
|
||||
{
|
||||
struct dma_debug_entry ref;
|
||||
|
||||
if (unlikely(dma_debug_disabled()))
|
||||
return;
|
||||
|
||||
ref.type = dma_debug_single;
|
||||
ref.dev = dev;
|
||||
ref.dev_addr = dma_handle;
|
||||
ref.size = offset + size;
|
||||
ref.direction = direction;
|
||||
ref.sg_call_ents = 0;
|
||||
|
||||
check_sync(dev, &ref, true);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_sync_single_range_for_cpu);
|
||||
|
||||
void debug_dma_sync_single_range_for_device(struct device *dev,
|
||||
dma_addr_t dma_handle,
|
||||
unsigned long offset,
|
||||
size_t size, int direction)
|
||||
{
|
||||
struct dma_debug_entry ref;
|
||||
|
||||
if (unlikely(dma_debug_disabled()))
|
||||
return;
|
||||
|
||||
ref.type = dma_debug_single;
|
||||
ref.dev = dev;
|
||||
ref.dev_addr = dma_handle;
|
||||
ref.size = offset + size;
|
||||
ref.direction = direction;
|
||||
ref.sg_call_ents = 0;
|
||||
|
||||
check_sync(dev, &ref, false);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_sync_single_range_for_device);
|
||||
|
||||
void debug_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
|
||||
int nelems, int direction)
|
||||
{
|
||||
@@ -1780,7 +1709,7 @@ static int __init dma_debug_driver_setup(char *str)
|
||||
}
|
||||
|
||||
if (current_driver_name[0])
|
||||
pr_info("DMA-API: enable driver filter for driver [%s]\n",
|
||||
pr_info("enable driver filter for driver [%s]\n",
|
||||
current_driver_name);
|
||||
|
||||
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include <linux/dma-noncoherent.h>
|
||||
#include <linux/pfn.h>
|
||||
#include <linux/set_memory.h>
|
||||
#include <linux/swiotlb.h>
|
||||
|
||||
/*
|
||||
* Most architectures use ZONE_DMA for the first 16 Megabytes, but
|
||||
@@ -30,27 +31,16 @@ static inline bool force_dma_unencrypted(void)
|
||||
return sev_active();
|
||||
}
|
||||
|
||||
static bool
|
||||
check_addr(struct device *dev, dma_addr_t dma_addr, size_t size,
|
||||
const char *caller)
|
||||
static void report_addr(struct device *dev, dma_addr_t dma_addr, size_t size)
|
||||
{
|
||||
if (unlikely(dev && !dma_capable(dev, dma_addr, size))) {
|
||||
if (!dev->dma_mask) {
|
||||
dev_err(dev,
|
||||
"%s: call on device without dma_mask\n",
|
||||
caller);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*dev->dma_mask >= DMA_BIT_MASK(32) || dev->bus_dma_mask) {
|
||||
dev_err(dev,
|
||||
"%s: overflow %pad+%zu of device mask %llx bus mask %llx\n",
|
||||
caller, &dma_addr, size,
|
||||
*dev->dma_mask, dev->bus_dma_mask);
|
||||
}
|
||||
return false;
|
||||
if (!dev->dma_mask) {
|
||||
dev_err_once(dev, "DMA map on device without dma_mask\n");
|
||||
} else if (*dev->dma_mask >= DMA_BIT_MASK(32) || dev->bus_dma_mask) {
|
||||
dev_err_once(dev,
|
||||
"overflow %pad+%zu of DMA mask %llx bus mask %llx\n",
|
||||
&dma_addr, size, *dev->dma_mask, dev->bus_dma_mask);
|
||||
}
|
||||
return true;
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
static inline dma_addr_t phys_to_dma_direct(struct device *dev,
|
||||
@@ -103,14 +93,13 @@ static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
|
||||
min_not_zero(dev->coherent_dma_mask, dev->bus_dma_mask);
|
||||
}
|
||||
|
||||
void *dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
|
||||
{
|
||||
unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
int page_order = get_order(size);
|
||||
struct page *page = NULL;
|
||||
u64 phys_mask;
|
||||
void *ret;
|
||||
|
||||
if (attrs & DMA_ATTR_NO_WARN)
|
||||
gfp |= __GFP_NOWARN;
|
||||
@@ -150,11 +139,34 @@ again:
|
||||
}
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
void *dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
|
||||
{
|
||||
struct page *page;
|
||||
void *ret;
|
||||
|
||||
page = __dma_direct_alloc_pages(dev, size, dma_handle, gfp, attrs);
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
if (PageHighMem(page)) {
|
||||
/*
|
||||
* Depending on the cma= arguments and per-arch setup
|
||||
* dma_alloc_from_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");
|
||||
__dma_direct_free_pages(dev, size, page);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = page_address(page);
|
||||
if (force_dma_unencrypted()) {
|
||||
set_memory_decrypted((unsigned long)ret, 1 << page_order);
|
||||
set_memory_decrypted((unsigned long)ret, 1 << get_order(size));
|
||||
*dma_handle = __phys_to_dma(dev, page_to_phys(page));
|
||||
} else {
|
||||
*dma_handle = phys_to_dma(dev, page_to_phys(page));
|
||||
@@ -163,20 +175,22 @@ again:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: this function must never look at the dma_addr argument, because we want
|
||||
* to be able to use it as a helper for iommu implementations as well.
|
||||
*/
|
||||
void __dma_direct_free_pages(struct device *dev, size_t size, struct page *page)
|
||||
{
|
||||
unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
|
||||
if (!dma_release_from_contiguous(dev, page, count))
|
||||
__free_pages(page, get_order(size));
|
||||
}
|
||||
|
||||
void dma_direct_free_pages(struct device *dev, size_t size, void *cpu_addr,
|
||||
dma_addr_t dma_addr, unsigned long attrs)
|
||||
{
|
||||
unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
unsigned int page_order = get_order(size);
|
||||
|
||||
if (force_dma_unencrypted())
|
||||
set_memory_encrypted((unsigned long)cpu_addr, 1 << page_order);
|
||||
if (!dma_release_from_contiguous(dev, virt_to_page(cpu_addr), count))
|
||||
free_pages((unsigned long)cpu_addr, page_order);
|
||||
__dma_direct_free_pages(dev, size, virt_to_page(cpu_addr));
|
||||
}
|
||||
|
||||
void *dma_direct_alloc(struct device *dev, size_t size,
|
||||
@@ -196,67 +210,111 @@ void dma_direct_free(struct device *dev, size_t size,
|
||||
dma_direct_free_pages(dev, size, cpu_addr, dma_addr, attrs);
|
||||
}
|
||||
|
||||
static void dma_direct_sync_single_for_device(struct device *dev,
|
||||
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
|
||||
defined(CONFIG_SWIOTLB)
|
||||
void dma_direct_sync_single_for_device(struct device *dev,
|
||||
dma_addr_t addr, size_t size, enum dma_data_direction dir)
|
||||
{
|
||||
if (dev_is_dma_coherent(dev))
|
||||
return;
|
||||
arch_sync_dma_for_device(dev, dma_to_phys(dev, addr), size, dir);
|
||||
}
|
||||
phys_addr_t paddr = dma_to_phys(dev, addr);
|
||||
|
||||
static void dma_direct_sync_sg_for_device(struct device *dev,
|
||||
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(dev, paddr, size, dir);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_direct_sync_single_for_device);
|
||||
|
||||
void dma_direct_sync_sg_for_device(struct device *dev,
|
||||
struct scatterlist *sgl, int nents, enum dma_data_direction dir)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
if (dev_is_dma_coherent(dev))
|
||||
return;
|
||||
for_each_sg(sgl, sg, nents, i) {
|
||||
if (unlikely(is_swiotlb_buffer(sg_phys(sg))))
|
||||
swiotlb_tbl_sync_single(dev, sg_phys(sg), sg->length,
|
||||
dir, SYNC_FOR_DEVICE);
|
||||
|
||||
for_each_sg(sgl, sg, nents, i)
|
||||
arch_sync_dma_for_device(dev, sg_phys(sg), sg->length, dir);
|
||||
if (!dev_is_dma_coherent(dev))
|
||||
arch_sync_dma_for_device(dev, sg_phys(sg), sg->length,
|
||||
dir);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(dma_direct_sync_sg_for_device);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
|
||||
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
|
||||
static void dma_direct_sync_single_for_cpu(struct device *dev,
|
||||
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) || \
|
||||
defined(CONFIG_SWIOTLB)
|
||||
void dma_direct_sync_single_for_cpu(struct device *dev,
|
||||
dma_addr_t addr, size_t size, enum dma_data_direction dir)
|
||||
{
|
||||
if (dev_is_dma_coherent(dev))
|
||||
return;
|
||||
arch_sync_dma_for_cpu(dev, dma_to_phys(dev, addr), size, dir);
|
||||
arch_sync_dma_for_cpu_all(dev);
|
||||
}
|
||||
phys_addr_t paddr = dma_to_phys(dev, addr);
|
||||
|
||||
static void dma_direct_sync_sg_for_cpu(struct device *dev,
|
||||
if (!dev_is_dma_coherent(dev)) {
|
||||
arch_sync_dma_for_cpu(dev, paddr, size, dir);
|
||||
arch_sync_dma_for_cpu_all(dev);
|
||||
}
|
||||
|
||||
if (unlikely(is_swiotlb_buffer(paddr)))
|
||||
swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_CPU);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_direct_sync_single_for_cpu);
|
||||
|
||||
void dma_direct_sync_sg_for_cpu(struct device *dev,
|
||||
struct scatterlist *sgl, int nents, enum dma_data_direction dir)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
if (dev_is_dma_coherent(dev))
|
||||
return;
|
||||
for_each_sg(sgl, sg, nents, i) {
|
||||
if (!dev_is_dma_coherent(dev))
|
||||
arch_sync_dma_for_cpu(dev, sg_phys(sg), sg->length, dir);
|
||||
|
||||
if (unlikely(is_swiotlb_buffer(sg_phys(sg))))
|
||||
swiotlb_tbl_sync_single(dev, sg_phys(sg), sg->length, dir,
|
||||
SYNC_FOR_CPU);
|
||||
}
|
||||
|
||||
for_each_sg(sgl, sg, nents, i)
|
||||
arch_sync_dma_for_cpu(dev, sg_phys(sg), sg->length, dir);
|
||||
arch_sync_dma_for_cpu_all(dev);
|
||||
if (!dev_is_dma_coherent(dev))
|
||||
arch_sync_dma_for_cpu_all(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_direct_sync_sg_for_cpu);
|
||||
|
||||
static void dma_direct_unmap_page(struct device *dev, dma_addr_t addr,
|
||||
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);
|
||||
}
|
||||
|
||||
static void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sgl,
|
||||
if (unlikely(is_swiotlb_buffer(phys)))
|
||||
swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_direct_unmap_page);
|
||||
|
||||
void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sgl,
|
||||
int nents, enum dma_data_direction dir, unsigned long attrs)
|
||||
{
|
||||
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
|
||||
dma_direct_sync_sg_for_cpu(dev, sgl, nents, dir);
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
for_each_sg(sgl, sg, nents, i)
|
||||
dma_direct_unmap_page(dev, sg->dma_address, sg_dma_len(sg), dir,
|
||||
attrs);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_direct_unmap_sg);
|
||||
#endif
|
||||
|
||||
static inline bool dma_direct_possible(struct device *dev, dma_addr_t dma_addr,
|
||||
size_t size)
|
||||
{
|
||||
return swiotlb_force != SWIOTLB_FORCE &&
|
||||
(!dev || dma_capable(dev, dma_addr, size));
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -264,13 +322,17 @@ dma_addr_t dma_direct_map_page(struct device *dev, struct page *page,
|
||||
phys_addr_t phys = page_to_phys(page) + offset;
|
||||
dma_addr_t dma_addr = phys_to_dma(dev, phys);
|
||||
|
||||
if (!check_addr(dev, dma_addr, size, __func__))
|
||||
return DIRECT_MAPPING_ERROR;
|
||||
if (unlikely(!dma_direct_possible(dev, dma_addr, size)) &&
|
||||
!swiotlb_map(dev, &phys, &dma_addr, size, dir, attrs)) {
|
||||
report_addr(dev, dma_addr, size);
|
||||
return DMA_MAPPING_ERROR;
|
||||
}
|
||||
|
||||
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
|
||||
dma_direct_sync_single_for_device(dev, dma_addr, size, dir);
|
||||
if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
|
||||
arch_sync_dma_for_device(dev, phys, size, dir);
|
||||
return dma_addr;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_direct_map_page);
|
||||
|
||||
int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
|
||||
enum dma_data_direction dir, unsigned long attrs)
|
||||
@@ -279,18 +341,20 @@ int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
|
||||
struct scatterlist *sg;
|
||||
|
||||
for_each_sg(sgl, sg, nents, i) {
|
||||
BUG_ON(!sg_page(sg));
|
||||
|
||||
sg_dma_address(sg) = phys_to_dma(dev, sg_phys(sg));
|
||||
if (!check_addr(dev, sg_dma_address(sg), sg->length, __func__))
|
||||
return 0;
|
||||
sg->dma_address = dma_direct_map_page(dev, sg_page(sg),
|
||||
sg->offset, sg->length, dir, attrs);
|
||||
if (sg->dma_address == DMA_MAPPING_ERROR)
|
||||
goto out_unmap;
|
||||
sg_dma_len(sg) = sg->length;
|
||||
}
|
||||
|
||||
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
|
||||
dma_direct_sync_sg_for_device(dev, sgl, nents, dir);
|
||||
return nents;
|
||||
|
||||
out_unmap:
|
||||
dma_direct_unmap_sg(dev, sgl, i, dir, attrs | DMA_ATTR_SKIP_CPU_SYNC);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_direct_map_sg);
|
||||
|
||||
/*
|
||||
* Because 32-bit DMA masks are so common we expect every architecture to be
|
||||
@@ -316,31 +380,3 @@ int dma_direct_supported(struct device *dev, u64 mask)
|
||||
*/
|
||||
return mask >= __phys_to_dma(dev, min_mask);
|
||||
}
|
||||
|
||||
int dma_direct_mapping_error(struct device *dev, dma_addr_t dma_addr)
|
||||
{
|
||||
return dma_addr == DIRECT_MAPPING_ERROR;
|
||||
}
|
||||
|
||||
const struct dma_map_ops dma_direct_ops = {
|
||||
.alloc = dma_direct_alloc,
|
||||
.free = dma_direct_free,
|
||||
.map_page = dma_direct_map_page,
|
||||
.map_sg = dma_direct_map_sg,
|
||||
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE)
|
||||
.sync_single_for_device = dma_direct_sync_single_for_device,
|
||||
.sync_sg_for_device = dma_direct_sync_sg_for_device,
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
|
||||
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
|
||||
.sync_single_for_cpu = dma_direct_sync_single_for_cpu,
|
||||
.sync_sg_for_cpu = dma_direct_sync_sg_for_cpu,
|
||||
.unmap_page = dma_direct_unmap_page,
|
||||
.unmap_sg = dma_direct_unmap_sg,
|
||||
#endif
|
||||
.get_required_mask = dma_direct_get_required_mask,
|
||||
.dma_supported = dma_direct_supported,
|
||||
.mapping_error = dma_direct_mapping_error,
|
||||
.cache_sync = arch_dma_cache_sync,
|
||||
};
|
||||
EXPORT_SYMBOL(dma_direct_ops);
|
||||
|
39
kernel/dma/dummy.c
Normal file
39
kernel/dma/dummy.c
Normal file
@@ -0,0 +1,39 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Dummy DMA ops that always fail.
|
||||
*/
|
||||
#include <linux/dma-mapping.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,
|
||||
unsigned long attrs)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static dma_addr_t dma_dummy_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 int dma_dummy_map_sg(struct device *dev, struct scatterlist *sgl,
|
||||
int nelems, enum dma_data_direction dir,
|
||||
unsigned long attrs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dma_dummy_supported(struct device *hwdev, u64 mask)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct dma_map_ops dma_dummy_ops = {
|
||||
.mmap = dma_dummy_mmap,
|
||||
.map_page = dma_dummy_map_page,
|
||||
.map_sg = dma_dummy_map_sg,
|
||||
.dma_supported = dma_dummy_supported,
|
||||
};
|
||||
EXPORT_SYMBOL(dma_dummy_ops);
|
@@ -5,8 +5,9 @@
|
||||
* Copyright (c) 2006 SUSE Linux Products GmbH
|
||||
* Copyright (c) 2006 Tejun Heo <teheo@suse.de>
|
||||
*/
|
||||
|
||||
#include <linux/memblock.h> /* for max_pfn */
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-noncoherent.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/gfp.h>
|
||||
@@ -223,7 +224,20 @@ int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
|
||||
sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_common_get_sgtable);
|
||||
|
||||
int dma_get_sgtable_attrs(struct device *dev, struct sg_table *sgt,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
unsigned long attrs)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
if (!dma_is_direct(ops) && ops->get_sgtable)
|
||||
return ops->get_sgtable(dev, sgt, cpu_addr, dma_addr, size,
|
||||
attrs);
|
||||
return dma_common_get_sgtable(dev, sgt, cpu_addr, dma_addr, size,
|
||||
attrs);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_get_sgtable_attrs);
|
||||
|
||||
/*
|
||||
* Create userspace mapping for the DMA-coherent memory.
|
||||
@@ -261,88 +275,179 @@ int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||
return -ENXIO;
|
||||
#endif /* !CONFIG_ARCH_NO_COHERENT_DMA_MMAP */
|
||||
}
|
||||
EXPORT_SYMBOL(dma_common_mmap);
|
||||
|
||||
#ifdef CONFIG_MMU
|
||||
static struct vm_struct *__dma_common_pages_remap(struct page **pages,
|
||||
size_t size, unsigned long vm_flags, pgprot_t prot,
|
||||
const void *caller)
|
||||
/**
|
||||
* dma_mmap_attrs - map a coherent DMA allocation into user space
|
||||
* @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
|
||||
* @vma: vm_area_struct describing requested user mapping
|
||||
* @cpu_addr: kernel CPU-view address returned from dma_alloc_attrs
|
||||
* @dma_addr: device-view address returned from dma_alloc_attrs
|
||||
* @size: size of memory originally requested in dma_alloc_attrs
|
||||
* @attrs: attributes of mapping properties requested in dma_alloc_attrs
|
||||
*
|
||||
* Map a coherent DMA buffer previously allocated by dma_alloc_attrs into user
|
||||
* space. The coherent DMA buffer must not be freed by the driver until the
|
||||
* user space mapping has been released.
|
||||
*/
|
||||
int dma_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
unsigned long attrs)
|
||||
{
|
||||
struct vm_struct *area;
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
area = get_vm_area_caller(size, vm_flags, caller);
|
||||
if (!area)
|
||||
return NULL;
|
||||
if (!dma_is_direct(ops) && ops->mmap)
|
||||
return ops->mmap(dev, vma, cpu_addr, dma_addr, size, attrs);
|
||||
return dma_common_mmap(dev, vma, cpu_addr, dma_addr, size, attrs);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_mmap_attrs);
|
||||
|
||||
if (map_vm_area(area, prot, pages)) {
|
||||
vunmap(area->addr);
|
||||
return NULL;
|
||||
#ifndef ARCH_HAS_DMA_GET_REQUIRED_MASK
|
||||
static u64 dma_default_get_required_mask(struct device *dev)
|
||||
{
|
||||
u32 low_totalram = ((max_pfn - 1) << PAGE_SHIFT);
|
||||
u32 high_totalram = ((max_pfn - 1) >> (32 - PAGE_SHIFT));
|
||||
u64 mask;
|
||||
|
||||
if (!high_totalram) {
|
||||
/* convert to mask just covering totalram */
|
||||
low_totalram = (1 << (fls(low_totalram) - 1));
|
||||
low_totalram += low_totalram - 1;
|
||||
mask = low_totalram;
|
||||
} else {
|
||||
high_totalram = (1 << (fls(high_totalram) - 1));
|
||||
high_totalram += high_totalram - 1;
|
||||
mask = (((u64)high_totalram) << 32) + 0xffffffff;
|
||||
}
|
||||
|
||||
return area;
|
||||
return mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* remaps an array of PAGE_SIZE pages into another vm_area
|
||||
* Cannot be used in non-sleeping contexts
|
||||
*/
|
||||
void *dma_common_pages_remap(struct page **pages, size_t size,
|
||||
unsigned long vm_flags, pgprot_t prot,
|
||||
const void *caller)
|
||||
u64 dma_get_required_mask(struct device *dev)
|
||||
{
|
||||
struct vm_struct *area;
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
area = __dma_common_pages_remap(pages, size, vm_flags, prot, caller);
|
||||
if (!area)
|
||||
return NULL;
|
||||
|
||||
area->pages = pages;
|
||||
|
||||
return area->addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* remaps an allocated contiguous region into another vm_area.
|
||||
* Cannot be used in non-sleeping contexts
|
||||
*/
|
||||
|
||||
void *dma_common_contiguous_remap(struct page *page, size_t size,
|
||||
unsigned long vm_flags,
|
||||
pgprot_t prot, const void *caller)
|
||||
{
|
||||
int i;
|
||||
struct page **pages;
|
||||
struct vm_struct *area;
|
||||
|
||||
pages = kmalloc(sizeof(struct page *) << get_order(size), GFP_KERNEL);
|
||||
if (!pages)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < (size >> PAGE_SHIFT); i++)
|
||||
pages[i] = nth_page(page, i);
|
||||
|
||||
area = __dma_common_pages_remap(pages, size, vm_flags, prot, caller);
|
||||
|
||||
kfree(pages);
|
||||
|
||||
if (!area)
|
||||
return NULL;
|
||||
return area->addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* unmaps a range previously mapped by dma_common_*_remap
|
||||
*/
|
||||
void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags)
|
||||
{
|
||||
struct vm_struct *area = find_vm_area(cpu_addr);
|
||||
|
||||
if (!area || (area->flags & vm_flags) != vm_flags) {
|
||||
WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr);
|
||||
return;
|
||||
}
|
||||
|
||||
unmap_kernel_range((unsigned long)cpu_addr, PAGE_ALIGN(size));
|
||||
vunmap(cpu_addr);
|
||||
if (dma_is_direct(ops))
|
||||
return dma_direct_get_required_mask(dev);
|
||||
if (ops->get_required_mask)
|
||||
return ops->get_required_mask(dev);
|
||||
return dma_default_get_required_mask(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_get_required_mask);
|
||||
#endif
|
||||
|
||||
#ifndef arch_dma_alloc_attrs
|
||||
#define arch_dma_alloc_attrs(dev) (true)
|
||||
#endif
|
||||
|
||||
void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
|
||||
gfp_t flag, unsigned long attrs)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
void *cpu_addr;
|
||||
|
||||
WARN_ON_ONCE(dev && !dev->coherent_dma_mask);
|
||||
|
||||
if (dma_alloc_from_dev_coherent(dev, size, dma_handle, &cpu_addr))
|
||||
return cpu_addr;
|
||||
|
||||
/* let the implementation decide on the zone to allocate from: */
|
||||
flag &= ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM);
|
||||
|
||||
if (!arch_dma_alloc_attrs(&dev))
|
||||
return NULL;
|
||||
|
||||
if (dma_is_direct(ops))
|
||||
cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs);
|
||||
else if (ops->alloc)
|
||||
cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs);
|
||||
else
|
||||
return NULL;
|
||||
|
||||
debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr);
|
||||
return cpu_addr;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_alloc_attrs);
|
||||
|
||||
void dma_free_attrs(struct device *dev, size_t size, void *cpu_addr,
|
||||
dma_addr_t dma_handle, unsigned long attrs)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
if (dma_release_from_dev_coherent(dev, get_order(size), cpu_addr))
|
||||
return;
|
||||
/*
|
||||
* On non-coherent platforms which implement DMA-coherent buffers via
|
||||
* non-cacheable remaps, ops->free() may call vunmap(). Thus getting
|
||||
* this far in IRQ context is a) at risk of a BUG_ON() or trying to
|
||||
* sleep on some machines, and b) an indication that the driver is
|
||||
* probably misusing the coherent API anyway.
|
||||
*/
|
||||
WARN_ON(irqs_disabled());
|
||||
|
||||
if (!cpu_addr)
|
||||
return;
|
||||
|
||||
debug_dma_free_coherent(dev, size, cpu_addr, dma_handle);
|
||||
if (dma_is_direct(ops))
|
||||
dma_direct_free(dev, size, cpu_addr, dma_handle, attrs);
|
||||
else if (ops->free)
|
||||
ops->free(dev, size, cpu_addr, dma_handle, attrs);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_free_attrs);
|
||||
|
||||
static inline void dma_check_mask(struct device *dev, u64 mask)
|
||||
{
|
||||
if (sme_active() && (mask < (((u64)sme_get_me_mask() << 1) - 1)))
|
||||
dev_warn(dev, "SME is active, device will require DMA bounce buffers\n");
|
||||
}
|
||||
|
||||
int dma_supported(struct device *dev, u64 mask)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
if (dma_is_direct(ops))
|
||||
return dma_direct_supported(dev, mask);
|
||||
if (!ops->dma_supported)
|
||||
return 1;
|
||||
return ops->dma_supported(dev, mask);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_supported);
|
||||
|
||||
#ifndef HAVE_ARCH_DMA_SET_MASK
|
||||
int dma_set_mask(struct device *dev, u64 mask)
|
||||
{
|
||||
if (!dev->dma_mask || !dma_supported(dev, mask))
|
||||
return -EIO;
|
||||
|
||||
dma_check_mask(dev, mask);
|
||||
*dev->dma_mask = mask;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_set_mask);
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ARCH_HAS_DMA_SET_COHERENT_MASK
|
||||
int dma_set_coherent_mask(struct device *dev, u64 mask)
|
||||
{
|
||||
if (!dma_supported(dev, mask))
|
||||
return -EIO;
|
||||
|
||||
dma_check_mask(dev, mask);
|
||||
dev->coherent_dma_mask = mask;
|
||||
return 0;
|
||||
}
|
||||
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_is_direct(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);
|
||||
|
256
kernel/dma/remap.c
Normal file
256
kernel/dma/remap.c
Normal file
@@ -0,0 +1,256 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2012 ARM Ltd.
|
||||
* Copyright (c) 2014 The Linux Foundation
|
||||
*/
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-noncoherent.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
static struct vm_struct *__dma_common_pages_remap(struct page **pages,
|
||||
size_t size, unsigned long vm_flags, pgprot_t prot,
|
||||
const void *caller)
|
||||
{
|
||||
struct vm_struct *area;
|
||||
|
||||
area = get_vm_area_caller(size, vm_flags, caller);
|
||||
if (!area)
|
||||
return NULL;
|
||||
|
||||
if (map_vm_area(area, prot, pages)) {
|
||||
vunmap(area->addr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return area;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remaps an array of PAGE_SIZE pages into another vm_area.
|
||||
* Cannot be used in non-sleeping contexts
|
||||
*/
|
||||
void *dma_common_pages_remap(struct page **pages, size_t size,
|
||||
unsigned long vm_flags, pgprot_t prot,
|
||||
const void *caller)
|
||||
{
|
||||
struct vm_struct *area;
|
||||
|
||||
area = __dma_common_pages_remap(pages, size, vm_flags, prot, caller);
|
||||
if (!area)
|
||||
return NULL;
|
||||
|
||||
area->pages = pages;
|
||||
|
||||
return area->addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remaps an allocated contiguous region into another vm_area.
|
||||
* Cannot be used in non-sleeping contexts
|
||||
*/
|
||||
void *dma_common_contiguous_remap(struct page *page, size_t size,
|
||||
unsigned long vm_flags,
|
||||
pgprot_t prot, const void *caller)
|
||||
{
|
||||
int i;
|
||||
struct page **pages;
|
||||
struct vm_struct *area;
|
||||
|
||||
pages = kmalloc(sizeof(struct page *) << get_order(size), GFP_KERNEL);
|
||||
if (!pages)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < (size >> PAGE_SHIFT); i++)
|
||||
pages[i] = nth_page(page, i);
|
||||
|
||||
area = __dma_common_pages_remap(pages, size, vm_flags, prot, caller);
|
||||
|
||||
kfree(pages);
|
||||
|
||||
if (!area)
|
||||
return NULL;
|
||||
return area->addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmaps a range previously mapped by dma_common_*_remap
|
||||
*/
|
||||
void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags)
|
||||
{
|
||||
struct vm_struct *area = find_vm_area(cpu_addr);
|
||||
|
||||
if (!area || (area->flags & vm_flags) != vm_flags) {
|
||||
WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr);
|
||||
return;
|
||||
}
|
||||
|
||||
unmap_kernel_range((unsigned long)cpu_addr, PAGE_ALIGN(size));
|
||||
vunmap(cpu_addr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DMA_DIRECT_REMAP
|
||||
static struct gen_pool *atomic_pool __ro_after_init;
|
||||
|
||||
#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K
|
||||
static size_t atomic_pool_size __initdata = DEFAULT_DMA_COHERENT_POOL_SIZE;
|
||||
|
||||
static int __init early_coherent_pool(char *p)
|
||||
{
|
||||
atomic_pool_size = memparse(p, &p);
|
||||
return 0;
|
||||
}
|
||||
early_param("coherent_pool", early_coherent_pool);
|
||||
|
||||
int __init dma_atomic_pool_init(gfp_t gfp, pgprot_t prot)
|
||||
{
|
||||
unsigned int pool_size_order = get_order(atomic_pool_size);
|
||||
unsigned long nr_pages = atomic_pool_size >> PAGE_SHIFT;
|
||||
struct page *page;
|
||||
void *addr;
|
||||
int ret;
|
||||
|
||||
if (dev_get_cma_area(NULL))
|
||||
page = dma_alloc_from_contiguous(NULL, nr_pages,
|
||||
pool_size_order, false);
|
||||
else
|
||||
page = alloc_pages(gfp, pool_size_order);
|
||||
if (!page)
|
||||
goto out;
|
||||
|
||||
arch_dma_prep_coherent(page, atomic_pool_size);
|
||||
|
||||
atomic_pool = gen_pool_create(PAGE_SHIFT, -1);
|
||||
if (!atomic_pool)
|
||||
goto free_page;
|
||||
|
||||
addr = dma_common_contiguous_remap(page, atomic_pool_size, VM_USERMAP,
|
||||
prot, __builtin_return_address(0));
|
||||
if (!addr)
|
||||
goto destroy_genpool;
|
||||
|
||||
ret = gen_pool_add_virt(atomic_pool, (unsigned long)addr,
|
||||
page_to_phys(page), atomic_pool_size, -1);
|
||||
if (ret)
|
||||
goto remove_mapping;
|
||||
gen_pool_set_algo(atomic_pool, gen_pool_first_fit_order_align, NULL);
|
||||
|
||||
pr_info("DMA: preallocated %zu KiB pool for atomic allocations\n",
|
||||
atomic_pool_size / 1024);
|
||||
return 0;
|
||||
|
||||
remove_mapping:
|
||||
dma_common_free_remap(addr, atomic_pool_size, VM_USERMAP);
|
||||
destroy_genpool:
|
||||
gen_pool_destroy(atomic_pool);
|
||||
atomic_pool = NULL;
|
||||
free_page:
|
||||
if (!dma_release_from_contiguous(NULL, page, nr_pages))
|
||||
__free_pages(page, pool_size_order);
|
||||
out:
|
||||
pr_err("DMA: failed to allocate %zu KiB pool for atomic coherent allocation\n",
|
||||
atomic_pool_size / 1024);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bool dma_in_atomic_pool(void *start, size_t size)
|
||||
{
|
||||
return addr_in_gen_pool(atomic_pool, (unsigned long)start, size);
|
||||
}
|
||||
|
||||
void *dma_alloc_from_pool(size_t size, struct page **ret_page, gfp_t flags)
|
||||
{
|
||||
unsigned long val;
|
||||
void *ptr = NULL;
|
||||
|
||||
if (!atomic_pool) {
|
||||
WARN(1, "coherent pool not initialised!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
val = gen_pool_alloc(atomic_pool, size);
|
||||
if (val) {
|
||||
phys_addr_t phys = gen_pool_virt_to_phys(atomic_pool, val);
|
||||
|
||||
*ret_page = pfn_to_page(__phys_to_pfn(phys));
|
||||
ptr = (void *)val;
|
||||
memset(ptr, 0, size);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bool dma_free_from_pool(void *start, size_t size)
|
||||
{
|
||||
if (!dma_in_atomic_pool(start, size))
|
||||
return false;
|
||||
gen_pool_free(atomic_pool, (unsigned long)start, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void *arch_dma_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
|
||||
gfp_t flags, unsigned long attrs)
|
||||
{
|
||||
struct page *page = NULL;
|
||||
void *ret;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
if (!gfpflags_allow_blocking(flags) &&
|
||||
!(attrs & DMA_ATTR_NO_KERNEL_MAPPING)) {
|
||||
ret = dma_alloc_from_pool(size, &page, flags);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
*dma_handle = phys_to_dma(dev, page_to_phys(page));
|
||||
return ret;
|
||||
}
|
||||
|
||||
page = __dma_direct_alloc_pages(dev, size, dma_handle, flags, attrs);
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
/* remove any dirty cache lines on the kernel alias */
|
||||
arch_dma_prep_coherent(page, size);
|
||||
|
||||
if (attrs & DMA_ATTR_NO_KERNEL_MAPPING)
|
||||
return page; /* opaque cookie */
|
||||
|
||||
/* create a coherent mapping */
|
||||
ret = dma_common_contiguous_remap(page, size, VM_USERMAP,
|
||||
arch_dma_mmap_pgprot(dev, PAGE_KERNEL, attrs),
|
||||
__builtin_return_address(0));
|
||||
if (!ret) {
|
||||
__dma_direct_free_pages(dev, size, page);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*dma_handle = phys_to_dma(dev, page_to_phys(page));
|
||||
memset(ret, 0, size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void arch_dma_free(struct device *dev, size_t size, void *vaddr,
|
||||
dma_addr_t dma_handle, unsigned long attrs)
|
||||
{
|
||||
if (attrs & DMA_ATTR_NO_KERNEL_MAPPING) {
|
||||
/* vaddr is a struct page cookie, not a kernel address */
|
||||
__dma_direct_free_pages(dev, size, vaddr);
|
||||
} else if (!dma_free_from_pool(vaddr, PAGE_ALIGN(size))) {
|
||||
phys_addr_t phys = dma_to_phys(dev, dma_handle);
|
||||
struct page *page = pfn_to_page(__phys_to_pfn(phys));
|
||||
|
||||
vunmap(vaddr);
|
||||
__dma_direct_free_pages(dev, size, page);
|
||||
}
|
||||
}
|
||||
|
||||
long arch_dma_coherent_to_pfn(struct device *dev, void *cpu_addr,
|
||||
dma_addr_t dma_addr)
|
||||
{
|
||||
return __phys_to_pfn(dma_to_phys(dev, dma_addr));
|
||||
}
|
||||
#endif /* CONFIG_DMA_DIRECT_REMAP */
|
@@ -21,7 +21,6 @@
|
||||
|
||||
#include <linux/cache.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-noncoherent.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/spinlock.h>
|
||||
@@ -65,7 +64,7 @@ enum swiotlb_force swiotlb_force;
|
||||
* swiotlb_tbl_sync_single_*, to see if the memory was in fact allocated by this
|
||||
* API.
|
||||
*/
|
||||
static phys_addr_t io_tlb_start, io_tlb_end;
|
||||
phys_addr_t io_tlb_start, io_tlb_end;
|
||||
|
||||
/*
|
||||
* The number of IO TLB blocks (in groups of 64) between io_tlb_start and
|
||||
@@ -383,11 +382,6 @@ void __init swiotlb_exit(void)
|
||||
max_segment = 0;
|
||||
}
|
||||
|
||||
static int is_swiotlb_buffer(phys_addr_t paddr)
|
||||
{
|
||||
return paddr >= io_tlb_start && paddr < io_tlb_end;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bounce: copy the swiotlb buffer back to the original dma location
|
||||
*/
|
||||
@@ -526,7 +520,7 @@ not_found:
|
||||
spin_unlock_irqrestore(&io_tlb_lock, flags);
|
||||
if (!(attrs & DMA_ATTR_NO_WARN) && printk_ratelimit())
|
||||
dev_warn(hwdev, "swiotlb buffer is full (sz: %zd bytes)\n", size);
|
||||
return SWIOTLB_MAP_ERROR;
|
||||
return DMA_MAPPING_ERROR;
|
||||
found:
|
||||
spin_unlock_irqrestore(&io_tlb_lock, flags);
|
||||
|
||||
@@ -623,237 +617,36 @@ void swiotlb_tbl_sync_single(struct device *hwdev, phys_addr_t tlb_addr,
|
||||
}
|
||||
}
|
||||
|
||||
static dma_addr_t swiotlb_bounce_page(struct device *dev, phys_addr_t *phys,
|
||||
/*
|
||||
* Create a swiotlb mapping for the buffer at @phys, and in case of DMAing
|
||||
* to the device copy the data into it as well.
|
||||
*/
|
||||
bool swiotlb_map(struct device *dev, phys_addr_t *phys, dma_addr_t *dma_addr,
|
||||
size_t size, enum dma_data_direction dir, unsigned long attrs)
|
||||
{
|
||||
dma_addr_t dma_addr;
|
||||
trace_swiotlb_bounced(dev, *dma_addr, size, swiotlb_force);
|
||||
|
||||
if (unlikely(swiotlb_force == SWIOTLB_NO_FORCE)) {
|
||||
dev_warn_ratelimited(dev,
|
||||
"Cannot do DMA to address %pa\n", phys);
|
||||
return DIRECT_MAPPING_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Oh well, have to allocate and map a bounce buffer. */
|
||||
*phys = swiotlb_tbl_map_single(dev, __phys_to_dma(dev, io_tlb_start),
|
||||
*phys, size, dir, attrs);
|
||||
if (*phys == SWIOTLB_MAP_ERROR)
|
||||
return DIRECT_MAPPING_ERROR;
|
||||
if (*phys == DMA_MAPPING_ERROR)
|
||||
return false;
|
||||
|
||||
/* Ensure that the address returned is DMA'ble */
|
||||
dma_addr = __phys_to_dma(dev, *phys);
|
||||
if (unlikely(!dma_capable(dev, dma_addr, size))) {
|
||||
*dma_addr = __phys_to_dma(dev, *phys);
|
||||
if (unlikely(!dma_capable(dev, *dma_addr, size))) {
|
||||
swiotlb_tbl_unmap_single(dev, *phys, size, dir,
|
||||
attrs | DMA_ATTR_SKIP_CPU_SYNC);
|
||||
return DIRECT_MAPPING_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
return dma_addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map a single buffer of the indicated size for DMA in streaming mode. The
|
||||
* physical address to use is returned.
|
||||
*
|
||||
* Once the device is given the dma address, the device owns this memory until
|
||||
* either swiotlb_unmap_page or swiotlb_dma_sync_single is performed.
|
||||
*/
|
||||
dma_addr_t swiotlb_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 dev_addr = phys_to_dma(dev, phys);
|
||||
|
||||
BUG_ON(dir == DMA_NONE);
|
||||
/*
|
||||
* If the address happens to be in the device's DMA window,
|
||||
* we can safely return the device addr and not worry about bounce
|
||||
* buffering it.
|
||||
*/
|
||||
if (!dma_capable(dev, dev_addr, size) ||
|
||||
swiotlb_force == SWIOTLB_FORCE) {
|
||||
trace_swiotlb_bounced(dev, dev_addr, size, swiotlb_force);
|
||||
dev_addr = swiotlb_bounce_page(dev, &phys, size, dir, attrs);
|
||||
}
|
||||
|
||||
if (!dev_is_dma_coherent(dev) &&
|
||||
(attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0 &&
|
||||
dev_addr != DIRECT_MAPPING_ERROR)
|
||||
arch_sync_dma_for_device(dev, phys, size, dir);
|
||||
|
||||
return dev_addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmap a single streaming mode DMA translation. The dma_addr and size must
|
||||
* match what was provided for in a previous swiotlb_map_page call. All
|
||||
* other usages are undefined.
|
||||
*
|
||||
* After this call, reads by the cpu to the buffer are guaranteed to see
|
||||
* whatever the device wrote there.
|
||||
*/
|
||||
void swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr,
|
||||
size_t size, enum dma_data_direction dir,
|
||||
unsigned long attrs)
|
||||
{
|
||||
phys_addr_t paddr = dma_to_phys(hwdev, dev_addr);
|
||||
|
||||
BUG_ON(dir == DMA_NONE);
|
||||
|
||||
if (!dev_is_dma_coherent(hwdev) &&
|
||||
(attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
|
||||
arch_sync_dma_for_cpu(hwdev, paddr, size, dir);
|
||||
|
||||
if (is_swiotlb_buffer(paddr)) {
|
||||
swiotlb_tbl_unmap_single(hwdev, paddr, size, dir, attrs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dir != DMA_FROM_DEVICE)
|
||||
return;
|
||||
|
||||
/*
|
||||
* phys_to_virt doesn't work with hihgmem page but we could
|
||||
* call dma_mark_clean() with hihgmem page here. However, we
|
||||
* are fine since dma_mark_clean() is null on POWERPC. We can
|
||||
* make dma_mark_clean() take a physical address if necessary.
|
||||
*/
|
||||
dma_mark_clean(phys_to_virt(paddr), size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make physical memory consistent for a single streaming mode DMA translation
|
||||
* after a transfer.
|
||||
*
|
||||
* If you perform a swiotlb_map_page() but wish to interrogate the buffer
|
||||
* using the cpu, yet do not wish to teardown the dma mapping, you must
|
||||
* call this function before doing so. At the next point you give the dma
|
||||
* address back to the card, you must first perform a
|
||||
* swiotlb_dma_sync_for_device, and then the device again owns the buffer
|
||||
*/
|
||||
static void
|
||||
swiotlb_sync_single(struct device *hwdev, dma_addr_t dev_addr,
|
||||
size_t size, enum dma_data_direction dir,
|
||||
enum dma_sync_target target)
|
||||
{
|
||||
phys_addr_t paddr = dma_to_phys(hwdev, dev_addr);
|
||||
|
||||
BUG_ON(dir == DMA_NONE);
|
||||
|
||||
if (!dev_is_dma_coherent(hwdev) && target == SYNC_FOR_CPU)
|
||||
arch_sync_dma_for_cpu(hwdev, paddr, size, dir);
|
||||
|
||||
if (is_swiotlb_buffer(paddr))
|
||||
swiotlb_tbl_sync_single(hwdev, paddr, size, dir, target);
|
||||
|
||||
if (!dev_is_dma_coherent(hwdev) && target == SYNC_FOR_DEVICE)
|
||||
arch_sync_dma_for_device(hwdev, paddr, size, dir);
|
||||
|
||||
if (!is_swiotlb_buffer(paddr) && dir == DMA_FROM_DEVICE)
|
||||
dma_mark_clean(phys_to_virt(paddr), size);
|
||||
}
|
||||
|
||||
void
|
||||
swiotlb_sync_single_for_cpu(struct device *hwdev, dma_addr_t dev_addr,
|
||||
size_t size, enum dma_data_direction dir)
|
||||
{
|
||||
swiotlb_sync_single(hwdev, dev_addr, size, dir, SYNC_FOR_CPU);
|
||||
}
|
||||
|
||||
void
|
||||
swiotlb_sync_single_for_device(struct device *hwdev, dma_addr_t dev_addr,
|
||||
size_t size, enum dma_data_direction dir)
|
||||
{
|
||||
swiotlb_sync_single(hwdev, dev_addr, size, dir, SYNC_FOR_DEVICE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Map a set of buffers described by scatterlist in streaming mode for DMA.
|
||||
* This is the scatter-gather version of the above swiotlb_map_page
|
||||
* interface. Here the scatter gather list elements are each tagged with the
|
||||
* appropriate dma address and length. They are obtained via
|
||||
* sg_dma_{address,length}(SG).
|
||||
*
|
||||
* Device ownership issues as mentioned above for swiotlb_map_page are the
|
||||
* same here.
|
||||
*/
|
||||
int
|
||||
swiotlb_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nelems,
|
||||
enum dma_data_direction dir, unsigned long attrs)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
for_each_sg(sgl, sg, nelems, i) {
|
||||
sg->dma_address = swiotlb_map_page(dev, sg_page(sg), sg->offset,
|
||||
sg->length, dir, attrs);
|
||||
if (sg->dma_address == DIRECT_MAPPING_ERROR)
|
||||
goto out_error;
|
||||
sg_dma_len(sg) = sg->length;
|
||||
}
|
||||
|
||||
return nelems;
|
||||
|
||||
out_error:
|
||||
swiotlb_unmap_sg_attrs(dev, sgl, i, dir,
|
||||
attrs | DMA_ATTR_SKIP_CPU_SYNC);
|
||||
sg_dma_len(sgl) = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmap a set of streaming mode DMA translations. Again, cpu read rules
|
||||
* concerning calls here are the same as for swiotlb_unmap_page() above.
|
||||
*/
|
||||
void
|
||||
swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl,
|
||||
int nelems, enum dma_data_direction dir,
|
||||
unsigned long attrs)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
BUG_ON(dir == DMA_NONE);
|
||||
|
||||
for_each_sg(sgl, sg, nelems, i)
|
||||
swiotlb_unmap_page(hwdev, sg->dma_address, sg_dma_len(sg), dir,
|
||||
attrs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make physical memory consistent for a set of streaming mode DMA translations
|
||||
* after a transfer.
|
||||
*
|
||||
* The same as swiotlb_sync_single_* but for a scatter-gather list, same rules
|
||||
* and usage.
|
||||
*/
|
||||
static void
|
||||
swiotlb_sync_sg(struct device *hwdev, struct scatterlist *sgl,
|
||||
int nelems, enum dma_data_direction dir,
|
||||
enum dma_sync_target target)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
for_each_sg(sgl, sg, nelems, i)
|
||||
swiotlb_sync_single(hwdev, sg->dma_address,
|
||||
sg_dma_len(sg), dir, target);
|
||||
}
|
||||
|
||||
void
|
||||
swiotlb_sync_sg_for_cpu(struct device *hwdev, struct scatterlist *sg,
|
||||
int nelems, enum dma_data_direction dir)
|
||||
{
|
||||
swiotlb_sync_sg(hwdev, sg, nelems, dir, SYNC_FOR_CPU);
|
||||
}
|
||||
|
||||
void
|
||||
swiotlb_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg,
|
||||
int nelems, enum dma_data_direction dir)
|
||||
{
|
||||
swiotlb_sync_sg(hwdev, sg, nelems, dir, SYNC_FOR_DEVICE);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -867,19 +660,3 @@ swiotlb_dma_supported(struct device *hwdev, u64 mask)
|
||||
{
|
||||
return __phys_to_dma(hwdev, io_tlb_end - 1) <= mask;
|
||||
}
|
||||
|
||||
const struct dma_map_ops swiotlb_dma_ops = {
|
||||
.mapping_error = dma_direct_mapping_error,
|
||||
.alloc = dma_direct_alloc,
|
||||
.free = dma_direct_free,
|
||||
.sync_single_for_cpu = swiotlb_sync_single_for_cpu,
|
||||
.sync_single_for_device = swiotlb_sync_single_for_device,
|
||||
.sync_sg_for_cpu = swiotlb_sync_sg_for_cpu,
|
||||
.sync_sg_for_device = swiotlb_sync_sg_for_device,
|
||||
.map_sg = swiotlb_map_sg_attrs,
|
||||
.unmap_sg = swiotlb_unmap_sg_attrs,
|
||||
.map_page = swiotlb_map_page,
|
||||
.unmap_page = swiotlb_unmap_page,
|
||||
.dma_supported = dma_direct_supported,
|
||||
};
|
||||
EXPORT_SYMBOL(swiotlb_dma_ops);
|
||||
|
@@ -13,7 +13,7 @@ static void *dma_virt_alloc(struct device *dev, size_t size,
|
||||
{
|
||||
void *ret;
|
||||
|
||||
ret = (void *)__get_free_pages(gfp, get_order(size));
|
||||
ret = (void *)__get_free_pages(gfp | __GFP_ZERO, get_order(size));
|
||||
if (ret)
|
||||
*dma_handle = (uintptr_t)ret;
|
||||
return ret;
|
||||
|
Reference in New Issue
Block a user