dma-mapping updates for Linux 6.13

- improve the DMA API tracing code (Sean Anderson)
  - misc cleanups (Christoph Hellwig, Sui Jingfeng)
  - fix pointer abuse when finding the shared DMA pool (Geert Uytterhoeven)
  - fix a deadlock in dma-debug (Levi Yun)
 -----BEGIN PGP SIGNATURE-----
 
 iQI/BAABCgApFiEEgdbnc3r/njty3Iq9D55TZVIEUYMFAmc8xN8LHGhjaEBsc3Qu
 ZGUACgkQD55TZVIEUYNwEBAAtd0zTiNuEUklY6YtZ7l/Zaudibmq1klHLGAQZEa9
 J4P2zzJ6xTkUblq/aVmFUQmf+vuuszjHIrrXnL3tAulSQKxS5Zj3Cci4cW4IAfBn
 GXB3OTR2lgXSk+8sulgiwc1AA8xgIFJJgZDTni1WdiW9LwLvUyYI1XNVAwCYOM2J
 HS2QxIySm3eg23F5bRz+Xl3LQlWYlHkMHryqKloHWIqchmVpYlYbj7uBMjAH4FKz
 l3zhd9pZSp9w5NNCp2Y/d81XdOUSjcYSR1gUotLzmW0Sj3YjnKXKdjjlPrj3zimb
 9EhgdalnpVrJ4Nr7MmpSUEbTVs+hBjXDoxTnnBRlKEl5aIKqceCrSBvoP70ygbkf
 KRqNS4ZxKe59cfnWAZQVcg8g01TetCoJR6QyGaoTE9Lz+9cPl2xAwyFmcYN2w/Cp
 qs0ZEFiNpqLAN5zwR/Pakz5YgIA/3N5MW0d9X9yEH9l4+HUMxWIF/qvThBSsGswT
 EmVUQqPpEzGJrcNYgC1UsEBltGmle02BwcoFEdMr7bzldW7yIpoDEOkKkBM3JFF9
 vgkpAkZGA5j4VMSkSwOrhi1rI0XAoImtJeM0wqhLtpXgQDjrMd3DaW6by6uUeH5x
 DcXf6qVOAsB04je9JkHh9I4BXVrWC01MSgFdjfQRl9gktn7970YFswG4ksYAwxU6
 xHQ=
 =ivZc
 -----END PGP SIGNATURE-----

Merge tag 'dma-mapping-6.13-2024-11-19' of git://git.infradead.org/users/hch/dma-mapping

Pull dma-mapping updates from Christoph Hellwig:

 - improve the DMA API tracing code (Sean Anderson)

 - misc cleanups (Christoph Hellwig, Sui Jingfeng)

 - fix pointer abuse when finding the shared DMA pool (Geert
   Uytterhoeven)

 - fix a deadlock in dma-debug (Levi Yun)

* tag 'dma-mapping-6.13-2024-11-19' of git://git.infradead.org/users/hch/dma-mapping:
  dma-mapping: save base/size instead of pointer to shared DMA pool
  dma-mapping: fix swapped dir/flags arguments to trace_dma_alloc_sgt_err
  dma-mapping: drop unneeded includes from dma-mapping.h
  dma-mapping: trace more error paths
  dma-mapping: use trace_dma_alloc for dma_alloc* instead of using trace_dma_map
  dma-mapping: trace dma_alloc/free direction
  dma-mapping: use macros to define events in a class
  dma-mapping: remove an outdated comment from dma-map-ops.h
  dma-debug: remove DMA_API_DEBUG_SG
  dma-debug: store a phys_addr_t in struct dma_debug_entry
  dma-debug: fix a possible deadlock on radix_lock
This commit is contained in:
Linus Torvalds 2024-11-21 11:28:39 -08:00
commit 51ae62a12c
8 changed files with 242 additions and 135 deletions

View File

@ -8,6 +8,7 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/memblock.h> #include <linux/memblock.h>
#include <linux/mem_encrypt.h>
#include <linux/cc_platform.h> #include <linux/cc_platform.h>
#include <asm/machdep.h> #include <asm/machdep.h>
#include <asm/svm.h> #include <asm/svm.h>

View File

@ -242,7 +242,7 @@ static inline bool dev_is_dma_coherent(struct device *dev)
{ {
return true; return true;
} }
#endif /* CONFIG_ARCH_HAS_DMA_COHERENCE_H */ #endif
static inline void dma_reset_need_sync(struct device *dev) static inline void dma_reset_need_sync(struct device *dev)
{ {

View File

@ -2,15 +2,11 @@
#ifndef _LINUX_DMA_MAPPING_H #ifndef _LINUX_DMA_MAPPING_H
#define _LINUX_DMA_MAPPING_H #define _LINUX_DMA_MAPPING_H
#include <linux/cache.h>
#include <linux/sizes.h>
#include <linux/string.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/dma-direction.h> #include <linux/dma-direction.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/mem_encrypt.h>
/** /**
* List of possible attributes associated with a DMA mapping. The semantics * List of possible attributes associated with a DMA mapping. The semantics

View File

@ -65,15 +65,14 @@ DECLARE_EVENT_CLASS(dma_map,
decode_dma_attrs(__entry->attrs)) decode_dma_attrs(__entry->attrs))
); );
DEFINE_EVENT(dma_map, dma_map_page, #define DEFINE_MAP_EVENT(name) \
TP_PROTO(struct device *dev, phys_addr_t phys_addr, dma_addr_t dma_addr, DEFINE_EVENT(dma_map, name, \
size_t size, enum dma_data_direction dir, unsigned long attrs), TP_PROTO(struct device *dev, phys_addr_t phys_addr, dma_addr_t dma_addr, \
TP_ARGS(dev, phys_addr, dma_addr, size, dir, attrs)); size_t size, enum dma_data_direction dir, unsigned long attrs), \
TP_ARGS(dev, phys_addr, dma_addr, size, dir, attrs))
DEFINE_EVENT(dma_map, dma_map_resource, DEFINE_MAP_EVENT(dma_map_page);
TP_PROTO(struct device *dev, phys_addr_t phys_addr, dma_addr_t dma_addr, DEFINE_MAP_EVENT(dma_map_resource);
size_t size, enum dma_data_direction dir, unsigned long attrs),
TP_ARGS(dev, phys_addr, dma_addr, size, dir, attrs));
DECLARE_EVENT_CLASS(dma_unmap, DECLARE_EVENT_CLASS(dma_unmap,
TP_PROTO(struct device *dev, dma_addr_t addr, size_t size, TP_PROTO(struct device *dev, dma_addr_t addr, size_t size,
@ -104,20 +103,20 @@ DECLARE_EVENT_CLASS(dma_unmap,
decode_dma_attrs(__entry->attrs)) decode_dma_attrs(__entry->attrs))
); );
DEFINE_EVENT(dma_unmap, dma_unmap_page, #define DEFINE_UNMAP_EVENT(name) \
TP_PROTO(struct device *dev, dma_addr_t addr, size_t size, DEFINE_EVENT(dma_unmap, name, \
enum dma_data_direction dir, unsigned long attrs), TP_PROTO(struct device *dev, dma_addr_t addr, size_t size, \
TP_ARGS(dev, addr, size, dir, attrs)); enum dma_data_direction dir, unsigned long attrs), \
TP_ARGS(dev, addr, size, dir, attrs))
DEFINE_EVENT(dma_unmap, dma_unmap_resource, DEFINE_UNMAP_EVENT(dma_unmap_page);
TP_PROTO(struct device *dev, dma_addr_t addr, size_t size, DEFINE_UNMAP_EVENT(dma_unmap_resource);
enum dma_data_direction dir, unsigned long attrs),
TP_ARGS(dev, addr, size, dir, attrs));
TRACE_EVENT(dma_alloc, DECLARE_EVENT_CLASS(dma_alloc_class,
TP_PROTO(struct device *dev, void *virt_addr, dma_addr_t dma_addr, TP_PROTO(struct device *dev, void *virt_addr, dma_addr_t dma_addr,
size_t size, gfp_t flags, unsigned long attrs), size_t size, enum dma_data_direction dir, gfp_t flags,
TP_ARGS(dev, virt_addr, dma_addr, size, flags, attrs), unsigned long attrs),
TP_ARGS(dev, virt_addr, dma_addr, size, dir, flags, attrs),
TP_STRUCT__entry( TP_STRUCT__entry(
__string(device, dev_name(dev)) __string(device, dev_name(dev))
@ -125,6 +124,7 @@ TRACE_EVENT(dma_alloc,
__field(u64, dma_addr) __field(u64, dma_addr)
__field(size_t, size) __field(size_t, size)
__field(gfp_t, flags) __field(gfp_t, flags)
__field(enum dma_data_direction, dir)
__field(unsigned long, attrs) __field(unsigned long, attrs)
), ),
@ -137,8 +137,9 @@ TRACE_EVENT(dma_alloc,
__entry->attrs = attrs; __entry->attrs = attrs;
), ),
TP_printk("%s dma_addr=%llx size=%zu virt_addr=%p flags=%s attrs=%s", TP_printk("%s dir=%s dma_addr=%llx size=%zu virt_addr=%p flags=%s attrs=%s",
__get_str(device), __get_str(device),
decode_dma_data_direction(__entry->dir),
__entry->dma_addr, __entry->dma_addr,
__entry->size, __entry->size,
__entry->virt_addr, __entry->virt_addr,
@ -146,16 +147,69 @@ TRACE_EVENT(dma_alloc,
decode_dma_attrs(__entry->attrs)) decode_dma_attrs(__entry->attrs))
); );
TRACE_EVENT(dma_free, #define DEFINE_ALLOC_EVENT(name) \
DEFINE_EVENT(dma_alloc_class, name, \
TP_PROTO(struct device *dev, void *virt_addr, dma_addr_t dma_addr, \
size_t size, enum dma_data_direction dir, gfp_t flags, \
unsigned long attrs), \
TP_ARGS(dev, virt_addr, dma_addr, size, dir, flags, attrs))
DEFINE_ALLOC_EVENT(dma_alloc);
DEFINE_ALLOC_EVENT(dma_alloc_pages);
DEFINE_ALLOC_EVENT(dma_alloc_sgt_err);
TRACE_EVENT(dma_alloc_sgt,
TP_PROTO(struct device *dev, struct sg_table *sgt, size_t size,
enum dma_data_direction dir, gfp_t flags, unsigned long attrs),
TP_ARGS(dev, sgt, size, dir, flags, attrs),
TP_STRUCT__entry(
__string(device, dev_name(dev))
__dynamic_array(u64, phys_addrs, sgt->orig_nents)
__field(u64, dma_addr)
__field(size_t, size)
__field(enum dma_data_direction, dir)
__field(gfp_t, flags)
__field(unsigned long, attrs)
),
TP_fast_assign(
struct scatterlist *sg;
int i;
__assign_str(device);
for_each_sg(sgt->sgl, sg, sgt->orig_nents, i)
((u64 *)__get_dynamic_array(phys_addrs))[i] = sg_phys(sg);
__entry->dma_addr = sg_dma_address(sgt->sgl);
__entry->size = size;
__entry->dir = dir;
__entry->flags = flags;
__entry->attrs = attrs;
),
TP_printk("%s dir=%s dma_addr=%llx size=%zu phys_addrs=%s flags=%s attrs=%s",
__get_str(device),
decode_dma_data_direction(__entry->dir),
__entry->dma_addr,
__entry->size,
__print_array(__get_dynamic_array(phys_addrs),
__get_dynamic_array_len(phys_addrs) /
sizeof(u64), sizeof(u64)),
show_gfp_flags(__entry->flags),
decode_dma_attrs(__entry->attrs))
);
DECLARE_EVENT_CLASS(dma_free_class,
TP_PROTO(struct device *dev, void *virt_addr, dma_addr_t dma_addr, TP_PROTO(struct device *dev, void *virt_addr, dma_addr_t dma_addr,
size_t size, unsigned long attrs), size_t size, enum dma_data_direction dir, unsigned long attrs),
TP_ARGS(dev, virt_addr, dma_addr, size, attrs), TP_ARGS(dev, virt_addr, dma_addr, size, dir, attrs),
TP_STRUCT__entry( TP_STRUCT__entry(
__string(device, dev_name(dev)) __string(device, dev_name(dev))
__field(void *, virt_addr) __field(void *, virt_addr)
__field(u64, dma_addr) __field(u64, dma_addr)
__field(size_t, size) __field(size_t, size)
__field(enum dma_data_direction, dir)
__field(unsigned long, attrs) __field(unsigned long, attrs)
), ),
@ -164,17 +218,63 @@ TRACE_EVENT(dma_free,
__entry->virt_addr = virt_addr; __entry->virt_addr = virt_addr;
__entry->dma_addr = dma_addr; __entry->dma_addr = dma_addr;
__entry->size = size; __entry->size = size;
__entry->dir = dir;
__entry->attrs = attrs; __entry->attrs = attrs;
), ),
TP_printk("%s dma_addr=%llx size=%zu virt_addr=%p attrs=%s", TP_printk("%s dir=%s dma_addr=%llx size=%zu virt_addr=%p attrs=%s",
__get_str(device), __get_str(device),
decode_dma_data_direction(__entry->dir),
__entry->dma_addr, __entry->dma_addr,
__entry->size, __entry->size,
__entry->virt_addr, __entry->virt_addr,
decode_dma_attrs(__entry->attrs)) decode_dma_attrs(__entry->attrs))
); );
#define DEFINE_FREE_EVENT(name) \
DEFINE_EVENT(dma_free_class, name, \
TP_PROTO(struct device *dev, void *virt_addr, dma_addr_t dma_addr, \
size_t size, enum dma_data_direction dir, unsigned long attrs), \
TP_ARGS(dev, virt_addr, dma_addr, size, dir, attrs))
DEFINE_FREE_EVENT(dma_free);
DEFINE_FREE_EVENT(dma_free_pages);
TRACE_EVENT(dma_free_sgt,
TP_PROTO(struct device *dev, struct sg_table *sgt, size_t size,
enum dma_data_direction dir),
TP_ARGS(dev, sgt, size, dir),
TP_STRUCT__entry(
__string(device, dev_name(dev))
__dynamic_array(u64, phys_addrs, sgt->orig_nents)
__field(u64, dma_addr)
__field(size_t, size)
__field(enum dma_data_direction, dir)
),
TP_fast_assign(
struct scatterlist *sg;
int i;
__assign_str(device);
for_each_sg(sgt->sgl, sg, sgt->orig_nents, i)
((u64 *)__get_dynamic_array(phys_addrs))[i] = sg_phys(sg);
__entry->dma_addr = sg_dma_address(sgt->sgl);
__entry->size = size;
__entry->dir = dir;
),
TP_printk("%s dir=%s dma_addr=%llx size=%zu phys_addrs=%s",
__get_str(device),
decode_dma_data_direction(__entry->dir),
__entry->dma_addr,
__entry->size,
__print_array(__get_dynamic_array(phys_addrs),
__get_dynamic_array_len(phys_addrs) /
sizeof(u64), sizeof(u64)))
);
TRACE_EVENT(dma_map_sg, TRACE_EVENT(dma_map_sg,
TP_PROTO(struct device *dev, struct scatterlist *sgl, int nents, TP_PROTO(struct device *dev, struct scatterlist *sgl, int nents,
int ents, enum dma_data_direction dir, unsigned long attrs), int ents, enum dma_data_direction dir, unsigned long attrs),
@ -221,6 +321,41 @@ TRACE_EVENT(dma_map_sg,
decode_dma_attrs(__entry->attrs)) decode_dma_attrs(__entry->attrs))
); );
TRACE_EVENT(dma_map_sg_err,
TP_PROTO(struct device *dev, struct scatterlist *sgl, int nents,
int err, enum dma_data_direction dir, unsigned long attrs),
TP_ARGS(dev, sgl, nents, err, dir, attrs),
TP_STRUCT__entry(
__string(device, dev_name(dev))
__dynamic_array(u64, phys_addrs, nents)
__field(int, err)
__field(enum dma_data_direction, dir)
__field(unsigned long, attrs)
),
TP_fast_assign(
struct scatterlist *sg;
int i;
__assign_str(device);
for_each_sg(sgl, sg, nents, i)
((u64 *)__get_dynamic_array(phys_addrs))[i] = sg_phys(sg);
__entry->err = err;
__entry->dir = dir;
__entry->attrs = attrs;
),
TP_printk("%s dir=%s dma_addrs=%s err=%d attrs=%s",
__get_str(device),
decode_dma_data_direction(__entry->dir),
__print_array(__get_dynamic_array(phys_addrs),
__get_dynamic_array_len(phys_addrs) /
sizeof(u64), sizeof(u64)),
__entry->err,
decode_dma_attrs(__entry->attrs))
);
TRACE_EVENT(dma_unmap_sg, TRACE_EVENT(dma_unmap_sg,
TP_PROTO(struct device *dev, struct scatterlist *sgl, int nents, TP_PROTO(struct device *dev, struct scatterlist *sgl, int nents,
enum dma_data_direction dir, unsigned long attrs), enum dma_data_direction dir, unsigned long attrs),
@ -279,15 +414,14 @@ DECLARE_EVENT_CLASS(dma_sync_single,
__entry->size) __entry->size)
); );
DEFINE_EVENT(dma_sync_single, dma_sync_single_for_cpu, #define DEFINE_SYNC_SINGLE_EVENT(name) \
TP_PROTO(struct device *dev, dma_addr_t dma_addr, size_t size, DEFINE_EVENT(dma_sync_single, name, \
enum dma_data_direction dir), TP_PROTO(struct device *dev, dma_addr_t dma_addr, size_t size, \
TP_ARGS(dev, dma_addr, size, dir)); enum dma_data_direction dir), \
TP_ARGS(dev, dma_addr, size, dir))
DEFINE_EVENT(dma_sync_single, dma_sync_single_for_device, DEFINE_SYNC_SINGLE_EVENT(dma_sync_single_for_cpu);
TP_PROTO(struct device *dev, dma_addr_t dma_addr, size_t size, DEFINE_SYNC_SINGLE_EVENT(dma_sync_single_for_device);
enum dma_data_direction dir),
TP_ARGS(dev, dma_addr, size, dir));
DECLARE_EVENT_CLASS(dma_sync_sg, DECLARE_EVENT_CLASS(dma_sync_sg,
TP_PROTO(struct device *dev, struct scatterlist *sgl, int nents, TP_PROTO(struct device *dev, struct scatterlist *sgl, int nents,
@ -326,15 +460,14 @@ DECLARE_EVENT_CLASS(dma_sync_sg,
sizeof(unsigned int), sizeof(unsigned int))) sizeof(unsigned int), sizeof(unsigned int)))
); );
DEFINE_EVENT(dma_sync_sg, dma_sync_sg_for_cpu, #define DEFINE_SYNC_SG_EVENT(name) \
TP_PROTO(struct device *dev, struct scatterlist *sg, int nents, DEFINE_EVENT(dma_sync_sg, name, \
enum dma_data_direction dir), TP_PROTO(struct device *dev, struct scatterlist *sg, int nents, \
TP_ARGS(dev, sg, nents, dir)); enum dma_data_direction dir), \
TP_ARGS(dev, sg, nents, dir))
DEFINE_EVENT(dma_sync_sg, dma_sync_sg_for_device, DEFINE_SYNC_SG_EVENT(dma_sync_sg_for_cpu);
TP_PROTO(struct device *dev, struct scatterlist *sg, int nents, DEFINE_SYNC_SG_EVENT(dma_sync_sg_for_device);
enum dma_data_direction dir),
TP_ARGS(dev, sg, nents, dir));
#endif /* _TRACE_DMA_H */ #endif /* _TRACE_DMA_H */

View File

@ -260,23 +260,6 @@ config DMA_API_DEBUG
If unsure, say N. If unsure, say N.
config DMA_API_DEBUG_SG
bool "Debug DMA scatter-gather usage"
default y
depends on DMA_API_DEBUG
help
Perform extra checking that callers of dma_map_sg() have respected the
appropriate segment length/boundary limits for the given device when
preparing DMA scatterlists.
This is particularly likely to have been overlooked in cases where the
dma_map_sg() API is used for general bulk mapping of pages rather than
preparing literal scatter-gather descriptors, where there is a risk of
unexpected behaviour from DMA API implementations if the scatterlist
is technically out-of-spec.
If unsure, say N.
config DMA_MAP_BENCHMARK config DMA_MAP_BENCHMARK
bool "Enable benchmarking of streaming DMA mapping" bool "Enable benchmarking of streaming DMA mapping"
depends on DEBUG_FS depends on DEBUG_FS

View File

@ -330,7 +330,8 @@ int dma_init_global_coherent(phys_addr_t phys_addr, size_t size)
#include <linux/of_reserved_mem.h> #include <linux/of_reserved_mem.h>
#ifdef CONFIG_DMA_GLOBAL_POOL #ifdef CONFIG_DMA_GLOBAL_POOL
static struct reserved_mem *dma_reserved_default_memory __initdata; static phys_addr_t dma_reserved_default_memory_base __initdata;
static phys_addr_t dma_reserved_default_memory_size __initdata;
#endif #endif
static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev) static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev)
@ -376,9 +377,10 @@ static int __init rmem_dma_setup(struct reserved_mem *rmem)
#ifdef CONFIG_DMA_GLOBAL_POOL #ifdef CONFIG_DMA_GLOBAL_POOL
if (of_get_flat_dt_prop(node, "linux,dma-default", NULL)) { if (of_get_flat_dt_prop(node, "linux,dma-default", NULL)) {
WARN(dma_reserved_default_memory, WARN(dma_reserved_default_memory_size,
"Reserved memory: region for default DMA coherent area is redefined\n"); "Reserved memory: region for default DMA coherent area is redefined\n");
dma_reserved_default_memory = rmem; dma_reserved_default_memory_base = rmem->base;
dma_reserved_default_memory_size = rmem->size;
} }
#endif #endif
@ -391,10 +393,10 @@ static int __init rmem_dma_setup(struct reserved_mem *rmem)
#ifdef CONFIG_DMA_GLOBAL_POOL #ifdef CONFIG_DMA_GLOBAL_POOL
static int __init dma_init_reserved_memory(void) static int __init dma_init_reserved_memory(void)
{ {
if (!dma_reserved_default_memory) if (!dma_reserved_default_memory_size)
return -ENOMEM; return -ENOMEM;
return dma_init_global_coherent(dma_reserved_default_memory->base, return dma_init_global_coherent(dma_reserved_default_memory_base,
dma_reserved_default_memory->size); dma_reserved_default_memory_size);
} }
core_initcall(dma_init_reserved_memory); core_initcall(dma_init_reserved_memory);
#endif /* CONFIG_DMA_GLOBAL_POOL */ #endif /* CONFIG_DMA_GLOBAL_POOL */

View File

@ -59,8 +59,7 @@ enum map_err_types {
* @direction: enum dma_data_direction * @direction: enum dma_data_direction
* @sg_call_ents: 'nents' from dma_map_sg * @sg_call_ents: 'nents' from dma_map_sg
* @sg_mapped_ents: 'mapped_ents' from dma_map_sg * @sg_mapped_ents: 'mapped_ents' from dma_map_sg
* @pfn: page frame of the start address * @paddr: physical start address of the mapping
* @offset: offset of mapping relative to pfn
* @map_err_type: track whether dma_mapping_error() was checked * @map_err_type: track whether dma_mapping_error() was checked
* @stack_len: number of backtrace entries in @stack_entries * @stack_len: number of backtrace entries in @stack_entries
* @stack_entries: stack of backtrace history * @stack_entries: stack of backtrace history
@ -74,8 +73,7 @@ struct dma_debug_entry {
int direction; int direction;
int sg_call_ents; int sg_call_ents;
int sg_mapped_ents; int sg_mapped_ents;
unsigned long pfn; phys_addr_t paddr;
size_t offset;
enum map_err_types map_err_type; enum map_err_types map_err_type;
#ifdef CONFIG_STACKTRACE #ifdef CONFIG_STACKTRACE
unsigned int stack_len; unsigned int stack_len;
@ -389,14 +387,6 @@ static void hash_bucket_del(struct dma_debug_entry *entry)
list_del(&entry->list); list_del(&entry->list);
} }
static unsigned long long phys_addr(struct dma_debug_entry *entry)
{
if (entry->type == dma_debug_resource)
return __pfn_to_phys(entry->pfn) + entry->offset;
return page_to_phys(pfn_to_page(entry->pfn)) + entry->offset;
}
/* /*
* For each mapping (initial cacheline in the case of * For each mapping (initial cacheline in the case of
* dma_alloc_coherent/dma_map_page, initial cacheline in each page of a * dma_alloc_coherent/dma_map_page, initial cacheline in each page of a
@ -428,8 +418,8 @@ static DEFINE_SPINLOCK(radix_lock);
static phys_addr_t to_cacheline_number(struct dma_debug_entry *entry) static phys_addr_t to_cacheline_number(struct dma_debug_entry *entry)
{ {
return (entry->pfn << CACHELINE_PER_PAGE_SHIFT) + return ((entry->paddr >> PAGE_SHIFT) << CACHELINE_PER_PAGE_SHIFT) +
(entry->offset >> L1_CACHE_SHIFT); (offset_in_page(entry->paddr) >> L1_CACHE_SHIFT);
} }
static int active_cacheline_read_overlap(phys_addr_t cln) static int active_cacheline_read_overlap(phys_addr_t cln)
@ -538,11 +528,11 @@ void debug_dma_dump_mappings(struct device *dev)
if (!dev || dev == entry->dev) { if (!dev || dev == entry->dev) {
cln = to_cacheline_number(entry); cln = to_cacheline_number(entry);
dev_info(entry->dev, dev_info(entry->dev,
"%s idx %d P=%llx N=%lx D=%llx L=%llx cln=%pa %s %s\n", "%s idx %d P=%pa D=%llx L=%llx cln=%pa %s %s\n",
type2name[entry->type], idx, type2name[entry->type], idx,
phys_addr(entry), entry->pfn, &entry->paddr, entry->dev_addr,
entry->dev_addr, entry->size, entry->size, &cln,
&cln, dir2name[entry->direction], dir2name[entry->direction],
maperr2str[entry->map_err_type]); maperr2str[entry->map_err_type]);
} }
} }
@ -569,13 +559,13 @@ static int dump_show(struct seq_file *seq, void *v)
list_for_each_entry(entry, &bucket->list, list) { list_for_each_entry(entry, &bucket->list, list) {
cln = to_cacheline_number(entry); cln = to_cacheline_number(entry);
seq_printf(seq, seq_printf(seq,
"%s %s %s idx %d P=%llx N=%lx D=%llx L=%llx cln=%pa %s %s\n", "%s %s %s idx %d P=%pa D=%llx L=%llx cln=%pa %s %s\n",
dev_driver_string(entry->dev), dev_driver_string(entry->dev),
dev_name(entry->dev), dev_name(entry->dev),
type2name[entry->type], idx, type2name[entry->type], idx,
phys_addr(entry), entry->pfn, &entry->paddr, entry->dev_addr,
entry->dev_addr, entry->size, entry->size, &cln,
&cln, dir2name[entry->direction], dir2name[entry->direction],
maperr2str[entry->map_err_type]); maperr2str[entry->map_err_type]);
} }
spin_unlock_irqrestore(&bucket->lock, flags); spin_unlock_irqrestore(&bucket->lock, flags);
@ -1003,16 +993,16 @@ static void check_unmap(struct dma_debug_entry *ref)
"[mapped as %s] [unmapped as %s]\n", "[mapped as %s] [unmapped as %s]\n",
ref->dev_addr, ref->size, ref->dev_addr, ref->size,
type2name[entry->type], type2name[ref->type]); type2name[entry->type], type2name[ref->type]);
} else if ((entry->type == dma_debug_coherent) && } else if (entry->type == dma_debug_coherent &&
(phys_addr(ref) != phys_addr(entry))) { ref->paddr != entry->paddr) {
err_printk(ref->dev, entry, "device driver frees " err_printk(ref->dev, entry, "device driver frees "
"DMA memory with different CPU address " "DMA memory with different CPU address "
"[device address=0x%016llx] [size=%llu bytes] " "[device address=0x%016llx] [size=%llu bytes] "
"[cpu alloc address=0x%016llx] " "[cpu alloc address=0x%pa] "
"[cpu free address=0x%016llx]", "[cpu free address=0x%pa]",
ref->dev_addr, ref->size, ref->dev_addr, ref->size,
phys_addr(entry), &entry->paddr,
phys_addr(ref)); &ref->paddr);
} }
if (ref->sg_call_ents && ref->type == dma_debug_sg && if (ref->sg_call_ents && ref->type == dma_debug_sg &&
@ -1052,9 +1042,13 @@ static void check_unmap(struct dma_debug_entry *ref)
} }
hash_bucket_del(entry); hash_bucket_del(entry);
dma_entry_free(entry);
put_hash_bucket(bucket, flags); put_hash_bucket(bucket, flags);
/*
* Free the entry outside of bucket_lock to avoid ABBA deadlocks
* between that and radix_lock.
*/
dma_entry_free(entry);
} }
static void check_for_stack(struct device *dev, static void check_for_stack(struct device *dev,
@ -1169,7 +1163,6 @@ out:
static void check_sg_segment(struct device *dev, struct scatterlist *sg) static void check_sg_segment(struct device *dev, struct scatterlist *sg)
{ {
#ifdef CONFIG_DMA_API_DEBUG_SG
unsigned int max_seg = dma_get_max_seg_size(dev); unsigned int max_seg = dma_get_max_seg_size(dev);
u64 start, end, boundary = dma_get_seg_boundary(dev); u64 start, end, boundary = dma_get_seg_boundary(dev);
@ -1190,7 +1183,6 @@ static void check_sg_segment(struct device *dev, struct scatterlist *sg)
if ((start ^ end) & ~boundary) if ((start ^ end) & ~boundary)
err_printk(dev, NULL, "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); start, end, boundary);
#endif
} }
void debug_dma_map_single(struct device *dev, const void *addr, void debug_dma_map_single(struct device *dev, const void *addr,
@ -1227,8 +1219,7 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset,
entry->dev = dev; entry->dev = dev;
entry->type = dma_debug_single; entry->type = dma_debug_single;
entry->pfn = page_to_pfn(page); entry->paddr = page_to_phys(page);
entry->offset = offset;
entry->dev_addr = dma_addr; entry->dev_addr = dma_addr;
entry->size = size; entry->size = size;
entry->direction = direction; entry->direction = direction;
@ -1323,8 +1314,7 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
entry->type = dma_debug_sg; entry->type = dma_debug_sg;
entry->dev = dev; entry->dev = dev;
entry->pfn = page_to_pfn(sg_page(s)); entry->paddr = sg_phys(s);
entry->offset = s->offset;
entry->size = sg_dma_len(s); entry->size = sg_dma_len(s);
entry->dev_addr = sg_dma_address(s); entry->dev_addr = sg_dma_address(s);
entry->direction = direction; entry->direction = direction;
@ -1370,8 +1360,7 @@ void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
struct dma_debug_entry ref = { struct dma_debug_entry ref = {
.type = dma_debug_sg, .type = dma_debug_sg,
.dev = dev, .dev = dev,
.pfn = page_to_pfn(sg_page(s)), .paddr = sg_phys(s),
.offset = s->offset,
.dev_addr = sg_dma_address(s), .dev_addr = sg_dma_address(s),
.size = sg_dma_len(s), .size = sg_dma_len(s),
.direction = dir, .direction = dir,
@ -1410,16 +1399,12 @@ void debug_dma_alloc_coherent(struct device *dev, size_t size,
entry->type = dma_debug_coherent; entry->type = dma_debug_coherent;
entry->dev = dev; entry->dev = dev;
entry->offset = offset_in_page(virt); entry->paddr = page_to_phys((is_vmalloc_addr(virt) ?
vmalloc_to_page(virt) : virt_to_page(virt)));
entry->size = size; entry->size = size;
entry->dev_addr = dma_addr; entry->dev_addr = dma_addr;
entry->direction = DMA_BIDIRECTIONAL; entry->direction = DMA_BIDIRECTIONAL;
if (is_vmalloc_addr(virt))
entry->pfn = vmalloc_to_pfn(virt);
else
entry->pfn = page_to_pfn(virt_to_page(virt));
add_dma_entry(entry, attrs); add_dma_entry(entry, attrs);
} }
@ -1429,7 +1414,6 @@ void debug_dma_free_coherent(struct device *dev, size_t size,
struct dma_debug_entry ref = { struct dma_debug_entry ref = {
.type = dma_debug_coherent, .type = dma_debug_coherent,
.dev = dev, .dev = dev,
.offset = offset_in_page(virt),
.dev_addr = dma_addr, .dev_addr = dma_addr,
.size = size, .size = size,
.direction = DMA_BIDIRECTIONAL, .direction = DMA_BIDIRECTIONAL,
@ -1439,10 +1423,8 @@ void debug_dma_free_coherent(struct device *dev, size_t size,
if (!is_vmalloc_addr(virt) && !virt_addr_valid(virt)) if (!is_vmalloc_addr(virt) && !virt_addr_valid(virt))
return; return;
if (is_vmalloc_addr(virt)) ref.paddr = page_to_phys((is_vmalloc_addr(virt) ?
ref.pfn = vmalloc_to_pfn(virt); vmalloc_to_page(virt) : virt_to_page(virt)));
else
ref.pfn = page_to_pfn(virt_to_page(virt));
if (unlikely(dma_debug_disabled())) if (unlikely(dma_debug_disabled()))
return; return;
@ -1465,8 +1447,7 @@ void debug_dma_map_resource(struct device *dev, phys_addr_t addr, size_t size,
entry->type = dma_debug_resource; entry->type = dma_debug_resource;
entry->dev = dev; entry->dev = dev;
entry->pfn = PHYS_PFN(addr); entry->paddr = addr;
entry->offset = offset_in_page(addr);
entry->size = size; entry->size = size;
entry->dev_addr = dma_addr; entry->dev_addr = dma_addr;
entry->direction = direction; entry->direction = direction;
@ -1543,8 +1524,7 @@ void debug_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
struct dma_debug_entry ref = { struct dma_debug_entry ref = {
.type = dma_debug_sg, .type = dma_debug_sg,
.dev = dev, .dev = dev,
.pfn = page_to_pfn(sg_page(s)), .paddr = sg_phys(s),
.offset = s->offset,
.dev_addr = sg_dma_address(s), .dev_addr = sg_dma_address(s),
.size = sg_dma_len(s), .size = sg_dma_len(s),
.direction = direction, .direction = direction,
@ -1575,8 +1555,7 @@ void debug_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
struct dma_debug_entry ref = { struct dma_debug_entry ref = {
.type = dma_debug_sg, .type = dma_debug_sg,
.dev = dev, .dev = dev,
.pfn = page_to_pfn(sg_page(s)), .paddr = sg_phys(sg),
.offset = s->offset,
.dev_addr = sg_dma_address(s), .dev_addr = sg_dma_address(s),
.size = sg_dma_len(s), .size = sg_dma_len(s),
.direction = direction, .direction = direction,

View File

@ -223,6 +223,7 @@ static int __dma_map_sg_attrs(struct device *dev, struct scatterlist *sg,
debug_dma_map_sg(dev, sg, nents, ents, dir, attrs); debug_dma_map_sg(dev, sg, nents, ents, dir, attrs);
} else if (WARN_ON_ONCE(ents != -EINVAL && ents != -ENOMEM && } else if (WARN_ON_ONCE(ents != -EINVAL && ents != -ENOMEM &&
ents != -EIO && ents != -EREMOTEIO)) { ents != -EIO && ents != -EREMOTEIO)) {
trace_dma_map_sg_err(dev, sg, nents, ents, dir, attrs);
return -EIO; return -EIO;
} }
@ -604,22 +605,29 @@ void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
if (WARN_ON_ONCE(flag & __GFP_COMP)) if (WARN_ON_ONCE(flag & __GFP_COMP))
return NULL; return NULL;
if (dma_alloc_from_dev_coherent(dev, size, dma_handle, &cpu_addr)) if (dma_alloc_from_dev_coherent(dev, size, dma_handle, &cpu_addr)) {
trace_dma_alloc(dev, cpu_addr, *dma_handle, size,
DMA_BIDIRECTIONAL, flag, attrs);
return cpu_addr; return cpu_addr;
}
/* let the implementation decide on the zone to allocate from: */ /* let the implementation decide on the zone to allocate from: */
flag &= ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM); flag &= ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM);
if (dma_alloc_direct(dev, ops)) if (dma_alloc_direct(dev, ops)) {
cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs); cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs);
else if (use_dma_iommu(dev)) } else if (use_dma_iommu(dev)) {
cpu_addr = iommu_dma_alloc(dev, size, dma_handle, flag, attrs); cpu_addr = iommu_dma_alloc(dev, size, dma_handle, flag, attrs);
else if (ops->alloc) } else if (ops->alloc) {
cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs); cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs);
else } else {
trace_dma_alloc(dev, NULL, 0, size, DMA_BIDIRECTIONAL, flag,
attrs);
return NULL; return NULL;
}
trace_dma_alloc(dev, cpu_addr, *dma_handle, size, flag, attrs); trace_dma_alloc(dev, cpu_addr, *dma_handle, size, DMA_BIDIRECTIONAL,
flag, attrs);
debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr, attrs); debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr, attrs);
return cpu_addr; return cpu_addr;
} }
@ -641,10 +649,11 @@ void dma_free_attrs(struct device *dev, size_t size, void *cpu_addr,
*/ */
WARN_ON(irqs_disabled()); WARN_ON(irqs_disabled());
trace_dma_free(dev, cpu_addr, dma_handle, size, DMA_BIDIRECTIONAL,
attrs);
if (!cpu_addr) if (!cpu_addr)
return; return;
trace_dma_free(dev, cpu_addr, dma_handle, size, attrs);
debug_dma_free_coherent(dev, size, cpu_addr, dma_handle); debug_dma_free_coherent(dev, size, cpu_addr, dma_handle);
if (dma_alloc_direct(dev, ops)) if (dma_alloc_direct(dev, ops))
dma_direct_free(dev, size, cpu_addr, dma_handle, attrs); dma_direct_free(dev, size, cpu_addr, dma_handle, attrs);
@ -683,9 +692,11 @@ struct page *dma_alloc_pages(struct device *dev, size_t size,
struct page *page = __dma_alloc_pages(dev, size, dma_handle, dir, gfp); struct page *page = __dma_alloc_pages(dev, size, dma_handle, dir, gfp);
if (page) { if (page) {
trace_dma_map_page(dev, page_to_phys(page), *dma_handle, size, trace_dma_alloc_pages(dev, page_to_virt(page), *dma_handle,
dir, 0); size, dir, gfp, 0);
debug_dma_map_page(dev, page, 0, size, dir, *dma_handle, 0); debug_dma_map_page(dev, page, 0, size, dir, *dma_handle, 0);
} else {
trace_dma_alloc_pages(dev, NULL, 0, size, dir, gfp, 0);
} }
return page; return page;
} }
@ -708,7 +719,7 @@ static void __dma_free_pages(struct device *dev, size_t size, struct page *page,
void dma_free_pages(struct device *dev, size_t size, struct page *page, void dma_free_pages(struct device *dev, size_t size, struct page *page,
dma_addr_t dma_handle, enum dma_data_direction dir) dma_addr_t dma_handle, enum dma_data_direction dir)
{ {
trace_dma_unmap_page(dev, dma_handle, size, dir, 0); trace_dma_free_pages(dev, page_to_virt(page), dma_handle, size, dir, 0);
debug_dma_unmap_page(dev, dma_handle, size, dir); debug_dma_unmap_page(dev, dma_handle, size, dir);
__dma_free_pages(dev, size, page, dma_handle, dir); __dma_free_pages(dev, size, page, dma_handle, dir);
} }
@ -768,8 +779,10 @@ struct sg_table *dma_alloc_noncontiguous(struct device *dev, size_t size,
if (sgt) { if (sgt) {
sgt->nents = 1; sgt->nents = 1;
trace_dma_map_sg(dev, sgt->sgl, sgt->orig_nents, 1, dir, attrs); trace_dma_alloc_sgt(dev, sgt, size, dir, gfp, attrs);
debug_dma_map_sg(dev, sgt->sgl, sgt->orig_nents, 1, dir, attrs); debug_dma_map_sg(dev, sgt->sgl, sgt->orig_nents, 1, dir, attrs);
} else {
trace_dma_alloc_sgt_err(dev, NULL, 0, size, dir, gfp, attrs);
} }
return sgt; return sgt;
} }
@ -787,7 +800,7 @@ static void free_single_sgt(struct device *dev, size_t size,
void dma_free_noncontiguous(struct device *dev, size_t size, void dma_free_noncontiguous(struct device *dev, size_t size,
struct sg_table *sgt, enum dma_data_direction dir) struct sg_table *sgt, enum dma_data_direction dir)
{ {
trace_dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir, 0); trace_dma_free_sgt(dev, sgt, size, dir);
debug_dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir); debug_dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir);
if (use_dma_iommu(dev)) if (use_dma_iommu(dev))