diff options
Diffstat (limited to 'lib/dma-debug.c')
-rw-r--r-- | lib/dma-debug.c | 88 |
1 files changed, 80 insertions, 8 deletions
diff --git a/lib/dma-debug.c b/lib/dma-debug.c index fcfa1939ac41..8971370bfb16 100644 --- a/lib/dma-debug.c +++ b/lib/dma-debug.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/stacktrace.h> | 22 | #include <linux/stacktrace.h> |
23 | #include <linux/dma-debug.h> | 23 | #include <linux/dma-debug.h> |
24 | #include <linux/spinlock.h> | 24 | #include <linux/spinlock.h> |
25 | #include <linux/vmalloc.h> | ||
25 | #include <linux/debugfs.h> | 26 | #include <linux/debugfs.h> |
26 | #include <linux/uaccess.h> | 27 | #include <linux/uaccess.h> |
27 | #include <linux/export.h> | 28 | #include <linux/export.h> |
@@ -43,6 +44,7 @@ enum { | |||
43 | dma_debug_page, | 44 | dma_debug_page, |
44 | dma_debug_sg, | 45 | dma_debug_sg, |
45 | dma_debug_coherent, | 46 | dma_debug_coherent, |
47 | dma_debug_resource, | ||
46 | }; | 48 | }; |
47 | 49 | ||
48 | enum map_err_types { | 50 | enum map_err_types { |
@@ -150,8 +152,9 @@ static const char *const maperr2str[] = { | |||
150 | [MAP_ERR_CHECKED] = "dma map error checked", | 152 | [MAP_ERR_CHECKED] = "dma map error checked", |
151 | }; | 153 | }; |
152 | 154 | ||
153 | static const char *type2name[4] = { "single", "page", | 155 | static const char *type2name[5] = { "single", "page", |
154 | "scather-gather", "coherent" }; | 156 | "scather-gather", "coherent", |
157 | "resource" }; | ||
155 | 158 | ||
156 | static const char *dir2name[4] = { "DMA_BIDIRECTIONAL", "DMA_TO_DEVICE", | 159 | static const char *dir2name[4] = { "DMA_BIDIRECTIONAL", "DMA_TO_DEVICE", |
157 | "DMA_FROM_DEVICE", "DMA_NONE" }; | 160 | "DMA_FROM_DEVICE", "DMA_NONE" }; |
@@ -399,6 +402,9 @@ static void hash_bucket_del(struct dma_debug_entry *entry) | |||
399 | 402 | ||
400 | static unsigned long long phys_addr(struct dma_debug_entry *entry) | 403 | static unsigned long long phys_addr(struct dma_debug_entry *entry) |
401 | { | 404 | { |
405 | if (entry->type == dma_debug_resource) | ||
406 | return __pfn_to_phys(entry->pfn) + entry->offset; | ||
407 | |||
402 | return page_to_phys(pfn_to_page(entry->pfn)) + entry->offset; | 408 | return page_to_phys(pfn_to_page(entry->pfn)) + entry->offset; |
403 | } | 409 | } |
404 | 410 | ||
@@ -1164,11 +1170,32 @@ static void check_unmap(struct dma_debug_entry *ref) | |||
1164 | put_hash_bucket(bucket, &flags); | 1170 | put_hash_bucket(bucket, &flags); |
1165 | } | 1171 | } |
1166 | 1172 | ||
1167 | static void check_for_stack(struct device *dev, void *addr) | 1173 | static void check_for_stack(struct device *dev, |
1174 | struct page *page, size_t offset) | ||
1168 | { | 1175 | { |
1169 | if (object_is_on_stack(addr)) | 1176 | void *addr; |
1170 | err_printk(dev, NULL, "DMA-API: device driver maps memory from " | 1177 | struct vm_struct *stack_vm_area = task_stack_vm_area(current); |
1171 | "stack [addr=%p]\n", addr); | 1178 | |
1179 | if (!stack_vm_area) { | ||
1180 | /* Stack is direct-mapped. */ | ||
1181 | if (PageHighMem(page)) | ||
1182 | return; | ||
1183 | addr = page_address(page) + offset; | ||
1184 | if (object_is_on_stack(addr)) | ||
1185 | err_printk(dev, NULL, "DMA-API: device driver maps memory from stack [addr=%p]\n", addr); | ||
1186 | } else { | ||
1187 | /* Stack is vmalloced. */ | ||
1188 | int i; | ||
1189 | |||
1190 | for (i = 0; i < stack_vm_area->nr_pages; i++) { | ||
1191 | if (page != stack_vm_area->pages[i]) | ||
1192 | continue; | ||
1193 | |||
1194 | addr = (u8 *)current->stack + i * PAGE_SIZE + offset; | ||
1195 | err_printk(dev, NULL, "DMA-API: device driver maps memory from stack [probable addr=%p]\n", addr); | ||
1196 | break; | ||
1197 | } | ||
1198 | } | ||
1172 | } | 1199 | } |
1173 | 1200 | ||
1174 | static inline bool overlap(void *addr, unsigned long len, void *start, void *end) | 1201 | static inline bool overlap(void *addr, unsigned long len, void *start, void *end) |
@@ -1291,10 +1318,11 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, | |||
1291 | if (map_single) | 1318 | if (map_single) |
1292 | entry->type = dma_debug_single; | 1319 | entry->type = dma_debug_single; |
1293 | 1320 | ||
1321 | check_for_stack(dev, page, offset); | ||
1322 | |||
1294 | if (!PageHighMem(page)) { | 1323 | if (!PageHighMem(page)) { |
1295 | void *addr = page_address(page) + offset; | 1324 | void *addr = page_address(page) + offset; |
1296 | 1325 | ||
1297 | check_for_stack(dev, addr); | ||
1298 | check_for_illegal_area(dev, addr, size); | 1326 | check_for_illegal_area(dev, addr, size); |
1299 | } | 1327 | } |
1300 | 1328 | ||
@@ -1386,8 +1414,9 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg, | |||
1386 | entry->sg_call_ents = nents; | 1414 | entry->sg_call_ents = nents; |
1387 | entry->sg_mapped_ents = mapped_ents; | 1415 | entry->sg_mapped_ents = mapped_ents; |
1388 | 1416 | ||
1417 | check_for_stack(dev, sg_page(s), s->offset); | ||
1418 | |||
1389 | if (!PageHighMem(sg_page(s))) { | 1419 | if (!PageHighMem(sg_page(s))) { |
1390 | check_for_stack(dev, sg_virt(s)); | ||
1391 | check_for_illegal_area(dev, sg_virt(s), sg_dma_len(s)); | 1420 | check_for_illegal_area(dev, sg_virt(s), sg_dma_len(s)); |
1392 | } | 1421 | } |
1393 | 1422 | ||
@@ -1495,6 +1524,49 @@ void debug_dma_free_coherent(struct device *dev, size_t size, | |||
1495 | } | 1524 | } |
1496 | EXPORT_SYMBOL(debug_dma_free_coherent); | 1525 | EXPORT_SYMBOL(debug_dma_free_coherent); |
1497 | 1526 | ||
1527 | void debug_dma_map_resource(struct device *dev, phys_addr_t addr, size_t size, | ||
1528 | int direction, dma_addr_t dma_addr) | ||
1529 | { | ||
1530 | struct dma_debug_entry *entry; | ||
1531 | |||
1532 | if (unlikely(dma_debug_disabled())) | ||
1533 | return; | ||
1534 | |||
1535 | entry = dma_entry_alloc(); | ||
1536 | if (!entry) | ||
1537 | return; | ||
1538 | |||
1539 | entry->type = dma_debug_resource; | ||
1540 | entry->dev = dev; | ||
1541 | entry->pfn = PHYS_PFN(addr); | ||
1542 | entry->offset = offset_in_page(addr); | ||
1543 | entry->size = size; | ||
1544 | entry->dev_addr = dma_addr; | ||
1545 | entry->direction = direction; | ||
1546 | entry->map_err_type = MAP_ERR_NOT_CHECKED; | ||
1547 | |||
1548 | add_dma_entry(entry); | ||
1549 | } | ||
1550 | EXPORT_SYMBOL(debug_dma_map_resource); | ||
1551 | |||
1552 | void debug_dma_unmap_resource(struct device *dev, dma_addr_t dma_addr, | ||
1553 | size_t size, int direction) | ||
1554 | { | ||
1555 | struct dma_debug_entry ref = { | ||
1556 | .type = dma_debug_resource, | ||
1557 | .dev = dev, | ||
1558 | .dev_addr = dma_addr, | ||
1559 | .size = size, | ||
1560 | .direction = direction, | ||
1561 | }; | ||
1562 | |||
1563 | if (unlikely(dma_debug_disabled())) | ||
1564 | return; | ||
1565 | |||
1566 | check_unmap(&ref); | ||
1567 | } | ||
1568 | EXPORT_SYMBOL(debug_dma_unmap_resource); | ||
1569 | |||
1498 | void debug_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, | 1570 | void debug_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, |
1499 | size_t size, int direction) | 1571 | size_t size, int direction) |
1500 | { | 1572 | { |