diff options
Diffstat (limited to 'lib/dma-debug.c')
| -rw-r--r-- | lib/dma-debug.c | 90 |
1 files changed, 82 insertions, 8 deletions
diff --git a/lib/dma-debug.c b/lib/dma-debug.c index 51a76af25c66..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" }; |
| @@ -253,6 +256,7 @@ static int hash_fn(struct dma_debug_entry *entry) | |||
| 253 | */ | 256 | */ |
| 254 | static struct hash_bucket *get_hash_bucket(struct dma_debug_entry *entry, | 257 | static struct hash_bucket *get_hash_bucket(struct dma_debug_entry *entry, |
| 255 | unsigned long *flags) | 258 | unsigned long *flags) |
| 259 | __acquires(&dma_entry_hash[idx].lock) | ||
| 256 | { | 260 | { |
| 257 | int idx = hash_fn(entry); | 261 | int idx = hash_fn(entry); |
| 258 | unsigned long __flags; | 262 | unsigned long __flags; |
| @@ -267,6 +271,7 @@ static struct hash_bucket *get_hash_bucket(struct dma_debug_entry *entry, | |||
| 267 | */ | 271 | */ |
| 268 | static void put_hash_bucket(struct hash_bucket *bucket, | 272 | static void put_hash_bucket(struct hash_bucket *bucket, |
| 269 | unsigned long *flags) | 273 | unsigned long *flags) |
| 274 | __releases(&bucket->lock) | ||
| 270 | { | 275 | { |
| 271 | unsigned long __flags = *flags; | 276 | unsigned long __flags = *flags; |
| 272 | 277 | ||
| @@ -397,6 +402,9 @@ static void hash_bucket_del(struct dma_debug_entry *entry) | |||
| 397 | 402 | ||
| 398 | static unsigned long long phys_addr(struct dma_debug_entry *entry) | 403 | static unsigned long long phys_addr(struct dma_debug_entry *entry) |
| 399 | { | 404 | { |
| 405 | if (entry->type == dma_debug_resource) | ||
| 406 | return __pfn_to_phys(entry->pfn) + entry->offset; | ||
| 407 | |||
| 400 | return page_to_phys(pfn_to_page(entry->pfn)) + entry->offset; | 408 | return page_to_phys(pfn_to_page(entry->pfn)) + entry->offset; |
| 401 | } | 409 | } |
| 402 | 410 | ||
| @@ -1162,11 +1170,32 @@ static void check_unmap(struct dma_debug_entry *ref) | |||
| 1162 | put_hash_bucket(bucket, &flags); | 1170 | put_hash_bucket(bucket, &flags); |
| 1163 | } | 1171 | } |
| 1164 | 1172 | ||
| 1165 | 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) | ||
| 1166 | { | 1175 | { |
| 1167 | if (object_is_on_stack(addr)) | 1176 | void *addr; |
| 1168 | err_printk(dev, NULL, "DMA-API: device driver maps memory from " | 1177 | struct vm_struct *stack_vm_area = task_stack_vm_area(current); |
| 1169 | "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 | } | ||
| 1170 | } | 1199 | } |
| 1171 | 1200 | ||
| 1172 | 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) |
| @@ -1289,10 +1318,11 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, | |||
| 1289 | if (map_single) | 1318 | if (map_single) |
| 1290 | entry->type = dma_debug_single; | 1319 | entry->type = dma_debug_single; |
| 1291 | 1320 | ||
| 1321 | check_for_stack(dev, page, offset); | ||
| 1322 | |||
| 1292 | if (!PageHighMem(page)) { | 1323 | if (!PageHighMem(page)) { |
| 1293 | void *addr = page_address(page) + offset; | 1324 | void *addr = page_address(page) + offset; |
| 1294 | 1325 | ||
| 1295 | check_for_stack(dev, addr); | ||
| 1296 | check_for_illegal_area(dev, addr, size); | 1326 | check_for_illegal_area(dev, addr, size); |
| 1297 | } | 1327 | } |
| 1298 | 1328 | ||
| @@ -1384,8 +1414,9 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg, | |||
| 1384 | entry->sg_call_ents = nents; | 1414 | entry->sg_call_ents = nents; |
| 1385 | entry->sg_mapped_ents = mapped_ents; | 1415 | entry->sg_mapped_ents = mapped_ents; |
| 1386 | 1416 | ||
| 1417 | check_for_stack(dev, sg_page(s), s->offset); | ||
| 1418 | |||
| 1387 | if (!PageHighMem(sg_page(s))) { | 1419 | if (!PageHighMem(sg_page(s))) { |
| 1388 | check_for_stack(dev, sg_virt(s)); | ||
| 1389 | 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)); |
| 1390 | } | 1421 | } |
| 1391 | 1422 | ||
| @@ -1493,6 +1524,49 @@ void debug_dma_free_coherent(struct device *dev, size_t size, | |||
| 1493 | } | 1524 | } |
| 1494 | EXPORT_SYMBOL(debug_dma_free_coherent); | 1525 | EXPORT_SYMBOL(debug_dma_free_coherent); |
| 1495 | 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 | |||
| 1496 | 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, |
| 1497 | size_t size, int direction) | 1571 | size_t size, int direction) |
| 1498 | { | 1572 | { |
