summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobin Murphy <robin.murphy@arm.com>2018-05-21 07:35:13 -0400
committerChristoph Hellwig <hch@lst.de>2018-05-24 03:24:17 -0400
commit78c47830a5cbb5d22dc91819a95af3d5c4bec084 (patch)
tree43665ea5d3d05e734ea6a1399e7e5fdfd06b7792
parent7f5c1ea3b707f349af539c7cb4ade8c9d0ad8452 (diff)
dma-debug: check scatterlist segments
Drivers/subsystems creating scatterlists for DMA should be taking care to respect the scatter-gather limitations of the appropriate device, as described by dma_parms. A DMA API implementation cannot feasibly split a scatterlist into *more* entries than originally passed, so it is not well defined what they should do when given a segment larger than the limit they are also required to respect. Conversely, devices which are less limited than the rather conservative defaults, or indeed have no limitations at all (e.g. GPUs with their own internal MMU), should be encouraged to set appropriate dma_parms, as they may get more efficient DMA mapping performance out of it. Signed-off-by: Robin Murphy <robin.murphy@arm.com> Signed-off-by: Christoph Hellwig <hch@lst.de>
-rw-r--r--lib/Kconfig.debug17
-rw-r--r--lib/dma-debug.c28
2 files changed, 45 insertions, 0 deletions
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index d5175eb7b917..76555479ae36 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1651,6 +1651,23 @@ config DMA_API_DEBUG
1651 1651
1652 If unsure, say N. 1652 If unsure, say N.
1653 1653
1654config DMA_API_DEBUG_SG
1655 bool "Debug DMA scatter-gather usage"
1656 default y
1657 depends on DMA_API_DEBUG
1658 help
1659 Perform extra checking that callers of dma_map_sg() have respected the
1660 appropriate segment length/boundary limits for the given device when
1661 preparing DMA scatterlists.
1662
1663 This is particularly likely to have been overlooked in cases where the
1664 dma_map_sg() API is used for general bulk mapping of pages rather than
1665 preparing literal scatter-gather descriptors, where there is a risk of
1666 unexpected behaviour from DMA API implementations if the scatterlist
1667 is technically out-of-spec.
1668
1669 If unsure, say N.
1670
1654menuconfig RUNTIME_TESTING_MENU 1671menuconfig RUNTIME_TESTING_MENU
1655 bool "Runtime Testing" 1672 bool "Runtime Testing"
1656 def_bool y 1673 def_bool y
diff --git a/lib/dma-debug.c b/lib/dma-debug.c
index 6a1ebaa83623..c007d25bee09 100644
--- a/lib/dma-debug.c
+++ b/lib/dma-debug.c
@@ -1286,6 +1286,32 @@ out:
1286 put_hash_bucket(bucket, &flags); 1286 put_hash_bucket(bucket, &flags);
1287} 1287}
1288 1288
1289static void check_sg_segment(struct device *dev, struct scatterlist *sg)
1290{
1291#ifdef CONFIG_DMA_API_DEBUG_SG
1292 unsigned int max_seg = dma_get_max_seg_size(dev);
1293 u64 start, end, boundary = dma_get_seg_boundary(dev);
1294
1295 /*
1296 * Either the driver forgot to set dma_parms appropriately, or
1297 * whoever generated the list forgot to check them.
1298 */
1299 if (sg->length > max_seg)
1300 err_printk(dev, NULL, "DMA-API: mapping sg segment longer than device claims to support [len=%u] [max=%u]\n",
1301 sg->length, max_seg);
1302 /*
1303 * In some cases this could potentially be the DMA API
1304 * implementation's fault, but it would usually imply that
1305 * the scatterlist was built inappropriately to begin with.
1306 */
1307 start = sg_dma_address(sg);
1308 end = start + sg_dma_len(sg) - 1;
1309 if ((start ^ end) & ~boundary)
1310 err_printk(dev, NULL, "DMA-API: mapping sg segment across boundary [start=0x%016llx] [end=0x%016llx] [boundary=0x%016llx]\n",
1311 start, end, boundary);
1312#endif
1313}
1314
1289void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, 1315void debug_dma_map_page(struct device *dev, struct page *page, size_t offset,
1290 size_t size, int direction, dma_addr_t dma_addr, 1316 size_t size, int direction, dma_addr_t dma_addr,
1291 bool map_single) 1317 bool map_single)
@@ -1416,6 +1442,8 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
1416 check_for_illegal_area(dev, sg_virt(s), sg_dma_len(s)); 1442 check_for_illegal_area(dev, sg_virt(s), sg_dma_len(s));
1417 } 1443 }
1418 1444
1445 check_sg_segment(dev, s);
1446
1419 add_dma_entry(entry); 1447 add_dma_entry(entry);
1420 } 1448 }
1421} 1449}