diff options
author | David Woodhouse <dwmw2@infradead.org> | 2009-01-19 10:52:39 -0500 |
---|---|---|
committer | Joerg Roedel <joerg.roedel@amd.com> | 2009-03-17 07:56:48 -0400 |
commit | 6c132d1bcdc716e898b4092bb1abc696505c37e7 (patch) | |
tree | 4000e2c7a7d87a80b1d70a29f4ed74227f1a826d | |
parent | 187f9c3f05373df4f7cbae2e656450acdbba7558 (diff) |
dma-debug: print stacktrace of mapping path on unmap error
Impact: saves stacktrace of a dma mapping and prints it if there is an error
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
-rw-r--r-- | lib/dma-debug.c | 52 |
1 files changed, 38 insertions, 14 deletions
diff --git a/lib/dma-debug.c b/lib/dma-debug.c index 91ed1dfdbaac..dba02f138bd3 100644 --- a/lib/dma-debug.c +++ b/lib/dma-debug.c | |||
@@ -19,6 +19,7 @@ | |||
19 | 19 | ||
20 | #include <linux/scatterlist.h> | 20 | #include <linux/scatterlist.h> |
21 | #include <linux/dma-mapping.h> | 21 | #include <linux/dma-mapping.h> |
22 | #include <linux/stacktrace.h> | ||
22 | #include <linux/dma-debug.h> | 23 | #include <linux/dma-debug.h> |
23 | #include <linux/spinlock.h> | 24 | #include <linux/spinlock.h> |
24 | #include <linux/debugfs.h> | 25 | #include <linux/debugfs.h> |
@@ -39,6 +40,8 @@ enum { | |||
39 | dma_debug_coherent, | 40 | dma_debug_coherent, |
40 | }; | 41 | }; |
41 | 42 | ||
43 | #define DMA_DEBUG_STACKTRACE_ENTRIES 5 | ||
44 | |||
42 | struct dma_debug_entry { | 45 | struct dma_debug_entry { |
43 | struct list_head list; | 46 | struct list_head list; |
44 | struct device *dev; | 47 | struct device *dev; |
@@ -49,6 +52,10 @@ struct dma_debug_entry { | |||
49 | int direction; | 52 | int direction; |
50 | int sg_call_ents; | 53 | int sg_call_ents; |
51 | int sg_mapped_ents; | 54 | int sg_mapped_ents; |
55 | #ifdef CONFIG_STACKTRACE | ||
56 | struct stack_trace stacktrace; | ||
57 | unsigned long st_entries[DMA_DEBUG_STACKTRACE_ENTRIES]; | ||
58 | #endif | ||
52 | }; | 59 | }; |
53 | 60 | ||
54 | struct hash_bucket { | 61 | struct hash_bucket { |
@@ -108,12 +115,23 @@ static const char *dir2name[4] = { "DMA_BIDIRECTIONAL", "DMA_TO_DEVICE", | |||
108 | * system log than the user configured. This variable is | 115 | * system log than the user configured. This variable is |
109 | * writeable via debugfs. | 116 | * writeable via debugfs. |
110 | */ | 117 | */ |
111 | #define err_printk(dev, format, arg...) do { \ | 118 | static inline void dump_entry_trace(struct dma_debug_entry *entry) |
119 | { | ||
120 | #ifdef CONFIG_STACKTRACE | ||
121 | if (entry) { | ||
122 | printk(KERN_WARNING "Mapped at:\n"); | ||
123 | print_stack_trace(&entry->stacktrace, 0); | ||
124 | } | ||
125 | #endif | ||
126 | } | ||
127 | |||
128 | #define err_printk(dev, entry, format, arg...) do { \ | ||
112 | error_count += 1; \ | 129 | error_count += 1; \ |
113 | if (show_all_errors || show_num_errors > 0) { \ | 130 | if (show_all_errors || show_num_errors > 0) { \ |
114 | WARN(1, "%s %s: " format, \ | 131 | WARN(1, "%s %s: " format, \ |
115 | dev_driver_string(dev), \ | 132 | dev_driver_string(dev), \ |
116 | dev_name(dev) , ## arg); \ | 133 | dev_name(dev) , ## arg); \ |
134 | dump_entry_trace(entry); \ | ||
117 | } \ | 135 | } \ |
118 | if (!show_all_errors && show_num_errors > 0) \ | 136 | if (!show_all_errors && show_num_errors > 0) \ |
119 | show_num_errors -= 1; \ | 137 | show_num_errors -= 1; \ |
@@ -260,6 +278,12 @@ static struct dma_debug_entry *dma_entry_alloc(void) | |||
260 | list_del(&entry->list); | 278 | list_del(&entry->list); |
261 | memset(entry, 0, sizeof(*entry)); | 279 | memset(entry, 0, sizeof(*entry)); |
262 | 280 | ||
281 | #ifdef CONFIG_STACKTRACE | ||
282 | entry->stacktrace.max_entries = DMA_DEBUG_STACKTRACE_ENTRIES; | ||
283 | entry->stacktrace.entries = entry->st_entries; | ||
284 | entry->stacktrace.skip = 2; | ||
285 | save_stack_trace(&entry->stacktrace); | ||
286 | #endif | ||
263 | num_free_entries -= 1; | 287 | num_free_entries -= 1; |
264 | if (num_free_entries < min_free_entries) | 288 | if (num_free_entries < min_free_entries) |
265 | min_free_entries = num_free_entries; | 289 | min_free_entries = num_free_entries; |
@@ -457,7 +481,7 @@ static void check_unmap(struct dma_debug_entry *ref) | |||
457 | entry = hash_bucket_find(bucket, ref); | 481 | entry = hash_bucket_find(bucket, ref); |
458 | 482 | ||
459 | if (!entry) { | 483 | if (!entry) { |
460 | err_printk(ref->dev, "DMA-API: device driver tries " | 484 | err_printk(ref->dev, NULL, "DMA-API: device driver tries " |
461 | "to free DMA memory it has not allocated " | 485 | "to free DMA memory it has not allocated " |
462 | "[device address=0x%016llx] [size=%llu bytes]\n", | 486 | "[device address=0x%016llx] [size=%llu bytes]\n", |
463 | ref->dev_addr, ref->size); | 487 | ref->dev_addr, ref->size); |
@@ -465,7 +489,7 @@ static void check_unmap(struct dma_debug_entry *ref) | |||
465 | } | 489 | } |
466 | 490 | ||
467 | if (ref->size != entry->size) { | 491 | if (ref->size != entry->size) { |
468 | err_printk(ref->dev, "DMA-API: device driver frees " | 492 | err_printk(ref->dev, entry, "DMA-API: device driver frees " |
469 | "DMA memory with different size " | 493 | "DMA memory with different size " |
470 | "[device address=0x%016llx] [map size=%llu bytes] " | 494 | "[device address=0x%016llx] [map size=%llu bytes] " |
471 | "[unmap size=%llu bytes]\n", | 495 | "[unmap size=%llu bytes]\n", |
@@ -473,7 +497,7 @@ static void check_unmap(struct dma_debug_entry *ref) | |||
473 | } | 497 | } |
474 | 498 | ||
475 | if (ref->type != entry->type) { | 499 | if (ref->type != entry->type) { |
476 | err_printk(ref->dev, "DMA-API: device driver frees " | 500 | err_printk(ref->dev, entry, "DMA-API: device driver frees " |
477 | "DMA memory with wrong function " | 501 | "DMA memory with wrong function " |
478 | "[device address=0x%016llx] [size=%llu bytes] " | 502 | "[device address=0x%016llx] [size=%llu bytes] " |
479 | "[mapped as %s] [unmapped as %s]\n", | 503 | "[mapped as %s] [unmapped as %s]\n", |
@@ -481,7 +505,7 @@ static void check_unmap(struct dma_debug_entry *ref) | |||
481 | type2name[entry->type], type2name[ref->type]); | 505 | type2name[entry->type], type2name[ref->type]); |
482 | } else if ((entry->type == dma_debug_coherent) && | 506 | } else if ((entry->type == dma_debug_coherent) && |
483 | (ref->paddr != entry->paddr)) { | 507 | (ref->paddr != entry->paddr)) { |
484 | err_printk(ref->dev, "DMA-API: device driver frees " | 508 | err_printk(ref->dev, entry, "DMA-API: device driver frees " |
485 | "DMA memory with different CPU address " | 509 | "DMA memory with different CPU address " |
486 | "[device address=0x%016llx] [size=%llu bytes] " | 510 | "[device address=0x%016llx] [size=%llu bytes] " |
487 | "[cpu alloc address=%p] [cpu free address=%p]", | 511 | "[cpu alloc address=%p] [cpu free address=%p]", |
@@ -491,7 +515,7 @@ static void check_unmap(struct dma_debug_entry *ref) | |||
491 | 515 | ||
492 | if (ref->sg_call_ents && ref->type == dma_debug_sg && | 516 | if (ref->sg_call_ents && ref->type == dma_debug_sg && |
493 | ref->sg_call_ents != entry->sg_call_ents) { | 517 | ref->sg_call_ents != entry->sg_call_ents) { |
494 | err_printk(ref->dev, "DMA-API: device driver frees " | 518 | err_printk(ref->dev, entry, "DMA-API: device driver frees " |
495 | "DMA sg list with different entry count " | 519 | "DMA sg list with different entry count " |
496 | "[map count=%d] [unmap count=%d]\n", | 520 | "[map count=%d] [unmap count=%d]\n", |
497 | entry->sg_call_ents, ref->sg_call_ents); | 521 | entry->sg_call_ents, ref->sg_call_ents); |
@@ -502,7 +526,7 @@ static void check_unmap(struct dma_debug_entry *ref) | |||
502 | * DMA API don't handle this properly, so check for it here | 526 | * DMA API don't handle this properly, so check for it here |
503 | */ | 527 | */ |
504 | if (ref->direction != entry->direction) { | 528 | if (ref->direction != entry->direction) { |
505 | err_printk(ref->dev, "DMA-API: device driver frees " | 529 | err_printk(ref->dev, entry, "DMA-API: device driver frees " |
506 | "DMA memory with different direction " | 530 | "DMA memory with different direction " |
507 | "[device address=0x%016llx] [size=%llu bytes] " | 531 | "[device address=0x%016llx] [size=%llu bytes] " |
508 | "[mapped with %s] [unmapped with %s]\n", | 532 | "[mapped with %s] [unmapped with %s]\n", |
@@ -521,8 +545,8 @@ out: | |||
521 | static void check_for_stack(struct device *dev, void *addr) | 545 | static void check_for_stack(struct device *dev, void *addr) |
522 | { | 546 | { |
523 | if (object_is_on_stack(addr)) | 547 | if (object_is_on_stack(addr)) |
524 | err_printk(dev, "DMA-API: device driver maps memory from stack" | 548 | err_printk(dev, NULL, "DMA-API: device driver maps memory from" |
525 | " [addr=%p]\n", addr); | 549 | "stack [addr=%p]\n", addr); |
526 | } | 550 | } |
527 | 551 | ||
528 | static void check_sync(struct device *dev, dma_addr_t addr, | 552 | static void check_sync(struct device *dev, dma_addr_t addr, |
@@ -543,7 +567,7 @@ static void check_sync(struct device *dev, dma_addr_t addr, | |||
543 | entry = hash_bucket_find(bucket, &ref); | 567 | entry = hash_bucket_find(bucket, &ref); |
544 | 568 | ||
545 | if (!entry) { | 569 | if (!entry) { |
546 | err_printk(dev, "DMA-API: device driver tries " | 570 | err_printk(dev, NULL, "DMA-API: device driver tries " |
547 | "to sync DMA memory it has not allocated " | 571 | "to sync DMA memory it has not allocated " |
548 | "[device address=0x%016llx] [size=%llu bytes]\n", | 572 | "[device address=0x%016llx] [size=%llu bytes]\n", |
549 | addr, size); | 573 | addr, size); |
@@ -551,7 +575,7 @@ static void check_sync(struct device *dev, dma_addr_t addr, | |||
551 | } | 575 | } |
552 | 576 | ||
553 | if ((offset + size) > entry->size) { | 577 | if ((offset + size) > entry->size) { |
554 | err_printk(dev, "DMA-API: device driver syncs" | 578 | err_printk(dev, entry, "DMA-API: device driver syncs" |
555 | " DMA memory outside allocated range " | 579 | " DMA memory outside allocated range " |
556 | "[device address=0x%016llx] " | 580 | "[device address=0x%016llx] " |
557 | "[allocation size=%llu bytes] [sync offset=%llu] " | 581 | "[allocation size=%llu bytes] [sync offset=%llu] " |
@@ -560,7 +584,7 @@ static void check_sync(struct device *dev, dma_addr_t addr, | |||
560 | } | 584 | } |
561 | 585 | ||
562 | if (direction != entry->direction) { | 586 | if (direction != entry->direction) { |
563 | err_printk(dev, "DMA-API: device driver syncs " | 587 | err_printk(dev, entry, "DMA-API: device driver syncs " |
564 | "DMA memory with different direction " | 588 | "DMA memory with different direction " |
565 | "[device address=0x%016llx] [size=%llu bytes] " | 589 | "[device address=0x%016llx] [size=%llu bytes] " |
566 | "[mapped with %s] [synced with %s]\n", | 590 | "[mapped with %s] [synced with %s]\n", |
@@ -574,7 +598,7 @@ static void check_sync(struct device *dev, dma_addr_t addr, | |||
574 | 598 | ||
575 | if (to_cpu && !(entry->direction == DMA_FROM_DEVICE) && | 599 | if (to_cpu && !(entry->direction == DMA_FROM_DEVICE) && |
576 | !(direction == DMA_TO_DEVICE)) | 600 | !(direction == DMA_TO_DEVICE)) |
577 | err_printk(dev, "DMA-API: device driver syncs " | 601 | err_printk(dev, entry, "DMA-API: device driver syncs " |
578 | "device read-only DMA memory for cpu " | 602 | "device read-only DMA memory for cpu " |
579 | "[device address=0x%016llx] [size=%llu bytes] " | 603 | "[device address=0x%016llx] [size=%llu bytes] " |
580 | "[mapped with %s] [synced with %s]\n", | 604 | "[mapped with %s] [synced with %s]\n", |
@@ -584,7 +608,7 @@ static void check_sync(struct device *dev, dma_addr_t addr, | |||
584 | 608 | ||
585 | if (!to_cpu && !(entry->direction == DMA_TO_DEVICE) && | 609 | if (!to_cpu && !(entry->direction == DMA_TO_DEVICE) && |
586 | !(direction == DMA_FROM_DEVICE)) | 610 | !(direction == DMA_FROM_DEVICE)) |
587 | err_printk(dev, "DMA-API: device driver syncs " | 611 | err_printk(dev, entry, "DMA-API: device driver syncs " |
588 | "device write-only DMA memory to device " | 612 | "device write-only DMA memory to device " |
589 | "[device address=0x%016llx] [size=%llu bytes] " | 613 | "[device address=0x%016llx] [size=%llu bytes] " |
590 | "[mapped with %s] [synced with %s]\n", | 614 | "[mapped with %s] [synced with %s]\n", |