aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Boyd <swboyd@chromium.org>2018-10-08 03:20:07 -0400
committerChristoph Hellwig <hch@lst.de>2018-10-08 03:44:17 -0400
commit99c65fa7c59ff558e70db8aa61bbdece5d3a9588 (patch)
tree5a72a54e4d6d2104b3aeedd86d84563d2d545af5
parent1fc8e6423edb4bba365b0780c2fcddfb921b24b2 (diff)
dma-debug: Check for drivers mapping invalid addresses in dma_map_single()
I recently debugged a DMA mapping oops where a driver was trying to map a buffer returned from request_firmware() with dma_map_single(). Memory returned from request_firmware() is mapped into the vmalloc region and this isn't a valid region to map with dma_map_single() per the DMA documentation's "What memory is DMA'able?" section. Unfortunately, we don't really check that in the DMA debugging code, so enabling DMA debugging doesn't help catch this problem. Let's add a new DMA debug function to check for a vmalloc address or an invalid virtual address and print a warning if this happens. This makes it a little easier to debug these sorts of problems, instead of seeing odd behavior or crashes when drivers attempt to map the vmalloc space for DMA. Cc: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Robin Murphy <robin.murphy@arm.com> Signed-off-by: Stephen Boyd <swboyd@chromium.org> Signed-off-by: Christoph Hellwig <hch@lst.de>
-rw-r--r--include/linux/dma-debug.h8
-rw-r--r--include/linux/dma-mapping.h1
-rw-r--r--kernel/dma/debug.c16
3 files changed, 25 insertions, 0 deletions
diff --git a/include/linux/dma-debug.h b/include/linux/dma-debug.h
index a785f2507159..30213adbb6b9 100644
--- a/include/linux/dma-debug.h
+++ b/include/linux/dma-debug.h
@@ -32,6 +32,9 @@ extern void dma_debug_add_bus(struct bus_type *bus);
32 32
33extern int dma_debug_resize_entries(u32 num_entries); 33extern int dma_debug_resize_entries(u32 num_entries);
34 34
35extern void debug_dma_map_single(struct device *dev, const void *addr,
36 unsigned long len);
37
35extern void debug_dma_map_page(struct device *dev, struct page *page, 38extern void debug_dma_map_page(struct device *dev, struct page *page,
36 size_t offset, size_t size, 39 size_t offset, size_t size,
37 int direction, dma_addr_t dma_addr, 40 int direction, dma_addr_t dma_addr,
@@ -103,6 +106,11 @@ static inline int dma_debug_resize_entries(u32 num_entries)
103 return 0; 106 return 0;
104} 107}
105 108
109static inline void debug_dma_map_single(struct device *dev, const void *addr,
110 unsigned long len)
111{
112}
113
106static inline void debug_dma_map_page(struct device *dev, struct page *page, 114static inline void debug_dma_map_page(struct device *dev, struct page *page,
107 size_t offset, size_t size, 115 size_t offset, size_t size,
108 int direction, dma_addr_t dma_addr, 116 int direction, dma_addr_t dma_addr,
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index 562af6b45f23..547a48bcfa3d 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -229,6 +229,7 @@ static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,
229 dma_addr_t addr; 229 dma_addr_t addr;
230 230
231 BUG_ON(!valid_dma_direction(dir)); 231 BUG_ON(!valid_dma_direction(dir));
232 debug_dma_map_single(dev, ptr, size);
232 addr = ops->map_page(dev, virt_to_page(ptr), 233 addr = ops->map_page(dev, virt_to_page(ptr),
233 offset_in_page(ptr), size, 234 offset_in_page(ptr), size,
234 dir, attrs); 235 dir, attrs);
diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c
index c007d25bee09..231ca4628062 100644
--- a/kernel/dma/debug.c
+++ b/kernel/dma/debug.c
@@ -1312,6 +1312,22 @@ static void check_sg_segment(struct device *dev, struct scatterlist *sg)
1312#endif 1312#endif
1313} 1313}
1314 1314
1315void debug_dma_map_single(struct device *dev, const void *addr,
1316 unsigned long len)
1317{
1318 if (unlikely(dma_debug_disabled()))
1319 return;
1320
1321 if (!virt_addr_valid(addr))
1322 err_printk(dev, NULL, "DMA-API: device driver maps memory from invalid area [addr=%p] [len=%lu]\n",
1323 addr, len);
1324
1325 if (is_vmalloc_addr(addr))
1326 err_printk(dev, NULL, "DMA-API: device driver maps memory from vmalloc area [addr=%p] [len=%lu]\n",
1327 addr, len);
1328}
1329EXPORT_SYMBOL(debug_dma_map_single);
1330
1315void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, 1331void debug_dma_map_page(struct device *dev, struct page *page, size_t offset,
1316 size_t size, int direction, dma_addr_t dma_addr, 1332 size_t size, int direction, dma_addr_t dma_addr,
1317 bool map_single) 1333 bool map_single)